반응형
시작하며
헥사고날 아키텍처를 사용해보고 싶어서 시작한 사이드 프로젝트
- 책이나 기술블로그, 세미나 등등 다양한 경로를 통해 접해봤지만 실제 적용해본 적은 없어서 코드를 작성해보며 체득하고 싶었다. 업무에서는 정해진 컨벤션이 있어서 시도해보기 어려워서 사이드 프로젝트에 적용해보면서 어떤 장점이 있는지 공부해보고 싶었다.
- 대략적인 개념은 만들면서 배우는 클린 아키텍처 도서를 보면서 공부했다. 얇은 책이지만 예시코드가 잘 나와있어서 기본적인 갈피를 잡는데 많은 도움이 됐다.
- 나름 DDD도 열심히 공부했는데 코드에 녹여보고 싶다.
- 기술스택 : Spring Boot, Spring Data JPA, Postresql
- 기회가 되면 redis도…
- 여유가 있으면 프론트 작업도 하면 재미있을 것 같다.
패키지 구성 - 카테고리 도메인
패키지 구조에 익숙하지 않아 간단한 기능부터 구현을 시작했다. 상품이 속한 카테고리를 표현하는 카테고리 도메인부터 구성했다. 상품 도메인을 구현할 때 필요하기도 하고 등록, 조회 정도면 충분하기 때문에 시작하기 좋을 것 같았다.
먼저 패키지 구조를 다음과 같이 구성했다.
📦fourdollar
┗ 📂category
┣ 📂adapter
┃ ┣ 📂in
┃ ┃ ┗ 📂web
┃ ┃ ┗ 📜CategoryRestController
┃ ┗ 📂out
┃ ┗ 📂persistence
┃ ┣ 📂jpa
┃ ┃ ┣ 📜CategoryJpaEntity
┃ ┃ ┗ 📜CategoryJpaRepository
┃ ┗ 📜CategoryPersistenceAdapter
┣ 📂domain
┃ ┗ 📜Category
┗ 📂application
┣ 📜RegisterCategoryService // 유즈케이스를 구현하는 서비스들
┣ 📜GetCategoryListService
┗ 📂port
┣ 📂in
┃ ┣ 📜RegisterCategoryUseCase
┃ ┗ 📜GetCategoryListUseCase
┗ 📂out
┣ 📜SaveCategoryPort
┗ 📜LoadCategoryPort
아직 시작단계이지만 이렇게 구성해보니 이런 점이 좋았다.
- 각 계층의 역할이 명확해져서 "이 코드를 어디에 작성해야 할까?" 하는 고민이 줄어들었다. 비즈니스 로직은 도메인에, 외부 연동은 어댑터에
- Repository를 세분화해서 서비스에서는 필요한 부분만 사용할 수 있었다. 테스트 코드 작성도 쉬워졌다.
하지만 패키지나 클래스를 너무 세분화해서 파일이 너무 많아져 귀찮은 부분도 있었다. 특히 각 계층마다 사용하는 dto 클래스를 정의했는데, 계층간 변환을 위한 매퍼 메서드 구현이 반복되는게 너무 번거로웠다.
마침 읽고 있던 클린 아키텍처 도서에 관련 내용이 나와있어서 나름 타협점을 찾을 수 있었다.
매핑 전략 선택하기
카테고리에 대한 유스게이스 별 경계간 매핑을 전략을 다르게 구성했다. 기본적으로 모든 유스케이스에 대해 애플리케이션 계층 ↔ 영속성 계층간 매핑은 양방향 매핑을 사용했다.
- 유스케이스마다 별도의 모델이 필요없이 도메인모델과 JPA 엔티티간 변환만 이루어지면 충분하다.
- JPA의 복잡한 연관관계와 도메인 로직을 분리하여 각 계층의 책임을 명확히 한다.
카테고리 등록
- 웹 계층 ↔ 애플리케이션 계층 : 완전 매핑
- 웹 계층과 도메인 로직을 분리한다.
- 유스케이스간 결합도를 없앤다.(ex: 수정 유스케이스에서는 별도의 클래스를 만든다)
- 애플리케이션 계층 ↔ 영속성 계층 : 양방향 매핑
// RegisterCategoryRequest는 웹 요청에 집중
// RegisterCategoryCommand는 비즈니스 로직에 집중
@PostMapping
RegisterCategoryResponse registerCategory(@RequestBody RegisterCategoryRequest request) {
return new RegisterCategoryResponse(registerCategoryUseCase.registerCategory(
new RegisterCategoryCommand(
request.parentId(),
request.name(),
request.description(),
request.displayOrder()
)
));
}
카테고리 조회
- 웹 계층 ↔ 애플리케이션 계층 : 매핑하지 않기
- 조회의 경우 단순하게 유지해도 충분하다.
- 불필요한 객체 생성이나 추가 매핑 코드가 없다.
- 애플리케이션 계층 ↔ 영속성 계층 : 양방향 매핑
// CategoryDetail은 애플리케이션 계층의 모델을 그대로 사용한다.
@GetMapping("/{categoryId}")
CategoryDetail getCategory(@PathVariable Long categoryId) {
return getCategoryUseCase.getCategory(categoryId);
}
현재 상품 도메인을 작업 중인데 JPA 엔티티를 도메인 모델로 사용할지 고민 중이다. JPA를 사용할 예정이고 다른 기술로 교체하지 않을 텐데 굳이 귀찮게 모델을 나누고 영속성 컨텍스트를 활용하지 못하는게 아쉽다. 이 부분은 좀 더 고민해보고 진행해야겠다.
반응형