Database Class Layer Structure C# vs Java
사실 하나의 Project 에서 여러개의 RDBMS 에 접속 해야 하는 경우는 흔치 않다. 하지만 항상 문제는 흔치 않은 작업을 하면서 생기지 않던가. 이번에 Spring+Kotlin+Exposed 를 기반으로 여러 DB를 사용 하도록 세팅 해 보니, 왜 EntityFramework 가 이런 구조를 가졌는지 좀 이해가 되었다.
아래 코드는 EFCore 공식 자료에 있는 샘플 코드이다
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class BloggingContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("ConnectionString");
}
}
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
public int Rating { get; set; }
public List<Post> Posts { get; set; }
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int BlogId { get; set; }
public Blog Blog { get; set; }
}
using (var db = new BloggingContext())
{
var blogs = await db.Blogs
.Where(b => b.Rating > 3)
.OrderBy(b => b.Url)
.ToListAsync();
}
최상위에 DBContext 객체가 있고, 그 Context가 Table 과 Mapping 되는 변수들을 들고 있다.
아래 코드는 Exposed 공식 자료에 있는 샘플 코드이다
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
object StarWarsFilmsTable : IntIdTable() {
val sequelId = integer("sequel_id").uniqueIndex()
val name = varchar("name", MAX_VARCHAR_LENGTH)
val director = varchar("director", MAX_VARCHAR_LENGTH)
}
class StarWarsFilmEntity(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<StarWarsFilmEntity>(StarWarsFilmsTable)
var sequelId by StarWarsFilmsTable.sequelId
var name by StarWarsFilmsTable.name
var director by StarWarsFilmsTable.director
}
val specificMovie = StarWarsFilmEntity.find { StarWarsFilmsTable.sequelId eq MOVIE_SEQUELID }
차이를 알겠는가. Exposed 는 Table 을 나타내는 Class 가 하나 추가되어있고, DB 자체를 나타내는 객체가 없다. 여기서는 궂이 언급 하지 않겠지만 SpringDataJPA 도 같은 구조를 가진다.
그럼 EF 에서처럼 DB Connection 을 가지고 있는 객체가 없는데, 어디에 연결하는 걸까? 이럴경우 Connection 은 TransactionManager 가 ThreadLocal 에 저장 해 둔 Connection 을 사용하게 된다.
어쨋든 위와 같이 쓰면 사용하는 입장에선 큰 차이가 없게 되는데… 문제는 여러개의 DB 를 동시에 사용 해야 할 경우 발생한다. 원래 관리 하지 않았던 Connection 을 명시적으로 컨트롤 해야하고, Entity가 어느 DB 에서 사용 가능한지 체크해야 하는 과정이 추가된다. 하필 이번엔 한개가 추가되는 것도 아니고 여러개의 DB가 동시에 추가되어서 새로 추가되는 Table 명에 익숙하지 않은것도 있어 굉장한 혼돈이 발생하였다. 기존 코드를 고치다가 커넥션 지정을 빼먹어 잘못된 DB 에 쿼리를 날리는 오류도 매우 많이 발생했다. 만약 EF 처럼 처음부터 관련 구조를 강제했다면 오히려 문제가 덜 생겼을 것도 같다.
혼돈을 없애기 위해 결국 DB 자체를 나타내는 상위 객체 레이어를 추가하고, 모든 쿼리에 Connection 을 명시할 수 있도록 코드를 수정하고 있다. 구조가 정리되면 다시한번 글을 써야겠다
MS가 확실히 큰 프로젝트를 많이 하는 회사라서 그런지, 만든 것을 보면 프로젝트가 발전하여 대규모가되거나 복잡해져도 기존 구조를 거의 바꾸지 않는 형태로 구현되는 경우가 많아보인다. 그 외 환경 특히 오픈소스는 Kick-Start 코드는 굉장히 심플하고 간단한데, 프로젝트가 커지면 대공사가 필요 한 경우가 많았고, 이번에도 그걸 느낀다.