이번 프로젝트는
"물류 관리 및 배송 시스템" 이다.
쿠팡 물류 시스템을 개발한다고 보면 될 것 같다
규모가 규모인 만큼 MSA 아키텍처로 설계하여 개발을 진행할 예정이다.
물론 서비스의 관점에서 보면 초기 빠른 개발을 통해서 MVP를 도출하려면 MSA는 적절한 아키텍처는 아니지만,
대규모 트래픽을 처리하는 물류관리 및 배송 시스템을 마련해야 한다는 상황을 가정하고,
개발을 시작할 것이다.
모든 주요 기능은 독립적인 마이크로 서비스로 개발할 것이고,
각각의 서비스는 독립적으로 배포되고 확장, 유지보수 될 수 있도록 설계할 것이다.
하지만, 모든 서비스를 MSA로 구성하지는 않을 것이다.
마이크로서비스는 보통 비즈니스 도메인에 맞춰 분리된다.
따라서 요구사항을 바탕으로 각 도메인과 마이크로서비스의 경계를 정의하는 것이 중요하다.
즉, 도메인 주도 설계가 필요하다는 것이다.
DDD 관점 프로젝트 패키지 구조 설계
기본적으로 4 Layered Architecture를 따를 것이며 DDD 적용을 위한 패키징 개념도 포함될 수 있다.
프로젝트 패키징 구조는 아래와 같은 예시를 사용할 것이다.
com.example.myapp
├── application
│ ├── service
│ │ ├── OrderService.java
│ ├── UserService.java
│ │ └── OrderMessageService.java
│ ├── dto
│ │ └── OrderDTO.java
├── domain
│ ├── model
│ │ ├── Order.java
│ │ ├── Product.java
│ │ └── ValueObject.java
│ ├── repository
│ │ └── OrderRepository.java
│ └── service
│ └── OrderDomainService.java
├── infrastructure
│ ├── repository
│ │ ├── JpaOrderRepository.java
│ │ ├── OrderRepositoryImpl.java
│ │ └── OrderQueryDSLRepositoryImpl.java
│ ├── client
│ │ └── UserClient.java
│ ├── configuration
│ │ └── DatabaseConfig.java
│ └── messaging
│ ├── OrderMessageConsumer.java
│ └── OrderMessageProducer.java
│
└── presentation
├── controller
│ └── OrderController.java
└── request
└── OrderRequest.java
위의 구조와 같이 크게,
application, domain, infrastructure, presentation 패키지 구조를 갖고 갈 것이다.
모놀리식 아키텍처 패키지 구조
모놀리식 아키텍처로 프로젝트를 개발할 때,
나는 항상 패키지 구조는 각 도메인 별로, Entity, Controller, Repository, Service, DTO 를 패키지로 구성했다.
전통적인 모듈형 패키징 방식을 사용한 것인데, 도메인 단위로 모든 계층을 한곳에 묶는 방법이다.
그래서 이런 단점이 생겼다
관심사 분리 부족: 도메인 로직과 인프라 로직이 섞일 가능성이 높아 유지보수가 어려워질 수 있습니다.
확장성 문제: 프로젝트가 커지면 패키지 내 파일이 많아지고, 의존성이 얽히면서 코드가 복잡해질 수 있습니다.
그래서 코드의 관심사를 명확히 분리하고, 도메인 로직을 중심으로 설계를 구성하는 것에 초점을 맞춰야 겠다는 생각이 들었다.
관심사를 분리하자
아래와 같이 먼저 관심사 분리를 진행한다. (각 계층은 책임을 명확히 나눈다.)
domain | 비즈니스 로직과 도메인 모델을 정의 (핵심 로직) |
application | 도메인 로직을 종합해 서비스 제공 |
infrastructure | 외부 시스템(DB, 메시징 등)과의 연동 |
presentation | 사용자 요청 처리와 응답 |
domain 패키지를 중심으로, 비지니스 로직이 다른 계층에 종속되지 않도록 한다.
domain의 코드는 핵심로직을 처리하고, infrastructure에는 이를 보조하는 역할의 코드가 존재하는 방식이다
물론, 이러한 방식이 절대적으로 옳다는 것이 아니다.
아무래도 프로젝트 규모에 따라 적절한 방식을 선택하는 것이 맞는 방식일 것이다
오히려 작은 프로젝트에서는 DDD를 고려한 패키징 구조는 오버엔지니어링이라고 생각한다
위의 패키지 구조를 보고 궁금한 점이 생길 수 있는데,
OrderRepository와 JpaOrderRepository가 왜 따로 있나요?
도메인 계층은 비즈니스 로직을 담당하는 곳입니다.
JpaRepository는 JPA의 QueryMethod, JPQL, NativeQuery 같은 특정 기술을 사용하기 때문에,
DDD의 관점에서는 도메인을 벗어난 외부 환경 요소에 해당합니다.
만약 도메인 계층이 JPA에 직접 의존하면,
향후 JPA가 아닌 다른 기술을 도입할 경우 도메인 계층까지 변경해야 하는 문제가 발생할 수 있습니다.
따라서, 비즈니스 로직 내에서는 JpaRepository를 직접 사용하지 않고,
도메인 계층에서는 데이터 접근 방식만 정의하는 인터페이스(OrderRepository)를 두어 도메인의 독립성을 가져갈 수 있습니다.
그리고 JPA를 사용하는 구체적인 구현체(JpaOrderRepository)는
Infrastructure 계층으로 분리하여 관리하는 것이 올바른 설계 방식입니다.

UserService와 UserClient의 차이는 무엇인가요?
MSA 환경에서 Order 서비스의 입장에서는 User 서비스는 비즈니스 내부가 아니라 외부 서비스로 간주되므로,
User와 관련된 설정은 Infrastructure 계층에 두는 것이 적합합니다.
여기서 문제는, 만약 OrderService가 Infrastructure에 위치한 UserService를 직접 사용하게 되면
Layered Architecture 원칙에 위배된다는 점입니다.
이를 해결하기 위해, UserService는 인터페이스로 정의하고,
실제 FeignClient 와 관련된 로직은 UserClient라는 별도의 파일로 분리하는 것이 좋습니다.
'Back-End > JavaSpring' 카테고리의 다른 글
JDBC가 등장한 이유와 JPA로 넘어간 이유 (3) | 2025.03.20 |
---|---|
MSA에서 API gateway를 통한 인증/인가를 처리하는 방식 (0) | 2025.03.14 |
DIP를 통해 반가운(?) 순환참조 해결하기 (springboot) (0) | 2025.03.10 |
요즘은 클라이언트에게 정적 페이지/동적 페이지가 아닌, 데이터를 반환해준다고? (Spring Boot) (0) | 2025.02.27 |
Jackson이란? (spring boot) (0) | 2025.02.25 |