고민의 시작은 '테이블 구조와 코드 구조의 불일치에서 발생하는 차이를 어떻게 해결해야할까?' 였다. 현재 내가 진행 중인 프로젝트의 게시판 테이블 구조는 다음과 같다.
이렇게 테이블 구조를 잡고 상속을 구현한 것까지는 문제가 없었다. 아니 사실은 똑같은 고민을 야기할 문제가 있었는데 이걸 인지하지 못했다고 하는게 더 적절할 것 같다. 그런데, 댓글을 적용하려고 하니 문제 상황을 알 수 있었다.
댓글까지 적용한 테이블 구조는 다음과 같다.
문제는 이걸 코드로 표현할 때 발생했다.
Board는 추상 클래스다. 코드 단위에서 요구 사항을 처리할 때는 FreeBoard, Review, Recruitment 세 개의 엔티티를 사용한다. 요구 사항에 해당되는 실질적인 객체는 위의 세 객체이기 때문에, 이들을 추상위한 상위 클래스는 추상 클래스로 처리한다. 그런데, 데이터베이스 관점에서 보면 세 객체는 모두 Board라는 하나의 테이블에 저장된다. 테이블 상속을 처음 구상할 때는 이 부분에 대한 생각까지는 이뤄지지 못했다.
테이블 상속 단계에서는 각 객체의 Repository를 따로 제작했다. 그런데 Comment를 제작하려고 보니, Comment는 Board에 담기는데 이걸 어떻게 처리해야할까를 고민하게 됐다. BoardRepository를 만들고 싶지는 않았다. Board 인스턴스를 만들 일이 유즈케이스 수준에서는 없기 때문에 Board 엔티티를 가져다 쓰는 Repository가 있는 것이 이상했다. 또한, Comment와의 연관관계를 생성하기 위한 조회 수준에서의 처리를 위해 BoardRepository 컴포넌트를 만든다는 것 자체도 지나치다고 생각했다.
사실 처음 상속을 할 때도 이런 임피던스 불일치 문제를 어느정도는 우려했지만, JPA에서 알아서 BOARD테이블에 넣어주니 별 생각 없이 넘어갔었다. 그런데 댓글 객체를 생각하니 이 문제가 더욱 직접적으로 다가왔다. 이 문제를 어떤 관점에서 어떻게 해결해야할지에 대해 고민했다.
해결 방법은 두 가지였다.
- BoardRepository를 만들고 Board를 구체 클래스로 전환한다. 댓글을 달 때는 Board인스턴스를 검색한다.
- 각 게시글 유형마다 댓글을 작성하는 API를 구분해서 제작한다. ReviewComment, FreeBoardComment, RecruitmentComment를 각각의 모듈로 구분하고 각 요구 사항을 처리한다.
이 부분에 대해 현업에 있는 친구에게 질문을 던졌고, 친구는 '코드 레벨에서의 관심과 데이터베이스 레벨의 관심을 분리해야 한다.'라는 답변을 줬다. 즉, 셋을 따로 처리해야한다는 것이다. 이에 더해 친구는 '그럼 코드 레벨에서도 API 영역, 도메인 영역, 데이터베이스 영역을 구분하려면 어떻게 해야할까?' 라는 질문을 던졌다.
확실히 이렇게 관심을 구분하는 것은 필요하다. API영역은 API영역의 일을 처리하고, 도메인 영역은 도메인 영역의 일을, 데이터베이스 영역은 데이터베이스 영역의 일을 처리해야한다. 그래야만이 단일 책임 원칙을 지키는 시스템 아키텍처를 구성할 수 있다. 그리고 이를 패키지 단위로 표현할 수 있어야 한다. 관심 영역을 패키지로 구분하면 세부적인 기능을 일일이 파악하지 않고 패키지만 보고도 시스템을 예측할 수 있다.
이제 내 패키지를 봤다. 문제가 많아보였다.
도메인 영역에 board가 있는 것은 좋다. board를 freeboard, recruitment, review로 분리한 것도 나쁘지 않다고 생각한다. Board 클래스를 따로 빼놓은 것도 일단은 뭐 상위 클래스를 먼저 보여줄 수 있어 그냥저냥 그렇다. 문제는 freeboard안에 controller, dto, entity, repository가 다 있다는 것이다.
freeboard는 domain이라는 단위 안에 속해 있다. 그런데, domain 단위 안에 controller, dto, repository, entity가 다 있는 건 이상하다. domain - API - database는 확연히 분리되어야 한다. 셋의 관심사는 완전히 다르며, 그렇기에 셋은 섞이면 안된다. 그런데 지금 내 코드에선 domain이 다른 관심사까지 다 포함하고 있다. 문제가 있어 보인다.
또한, 도메인 영역에서 사용되어야 할 FreeBoard클래스에 @Entity애너테이션이 붙어 '얘는 데이터베이스에 들어가는 애에요'라고 말하고 있다. 도메인 객체와 엔티티 객체가 혼용되고있는 것이다. 심지어 modify()에서는 putDto를 도메인 객체가 받는다(사실 도메인 객체라고 보기도 애매해져버린). 코드를 전체적으로 손봐야했다. 더 좋은 설계 방법과 이를 구현한 이들의 예시가 필요했다. 아키텍처에 대해 좀 더 찾아봤다.
헥사고날 아키텍처는 이런 고민의 해답이 되어보였다. 각 영역을 완벽히 분리하고, 특히 도메인 영역은 어떤 영역과도 분리되어 완벽하게 자율성을 보장받는다. 비즈니스 이슈를 해결하기 위한 코드 수정이나 추가, 삭제가 다른 영역에 방해받지 않고 이뤄진다. 아름다운 구조였다. 아마 다음 몇 개의 Study 포스팅들은 헥사고날 아키텍처에 대한 학습 기록과 이를 코드에 적용한 결과물이 되지 않을까 싶다.
'Programming > Study' 카테고리의 다른 글
계층별로 분리해서 DTO를 사용하기로 한 이유와 결과 (0) | 2023.12.13 |
---|---|
헥사고날 아키텍처 얕게 공부한 뒤 느낀점 - 기본이나 잘하라 (1) | 2023.12.11 |
Querydsl 적용한 Repository에 @DataJpaTest 적용하기 (1) | 2023.12.08 |
@BeforeEach vs @BeforeAll - 한 테스트 클래스에 여러 개의 테스트가 있으면 반드시 동시에 돌려봐야 하는 이유 / @TestInstace (2) | 2023.12.08 |
테스트를 실행은 하고 싶은데 빌드에서는 제외하고 싶을 때 (1) | 2023.12.07 |
댓글