Degging (2) - DDD 설계를 선택한 이유
DDD(Domain-Driven Design) 설계를 선택한 이유
프로젝트 구조를 설계할 때 가장 많이 고민한 부분이다.
전통적인 Spring Boot 프로젝트는 보통 계층형 구조(Layered Architecture)를 따른다.
1
2
3
4
5
src/
├── controller/
├── service/
├── repository/
└── entity/
이 구조는 단순한 CRUD 프로젝트에서는 잘 동작하지만, Degging처럼 도메인이 복잡한 서비스에서는 한계가 있다.
왜 계층형 구조가 안 맞았나
Degging의 도메인을 나열해보면:
- 인증(auth): 로그인, 회원가입, 토큰 관리
- 카페(cafe): 위치, 영업시간, 메뉴 등 정형 데이터
- 탐색(discovery): 카페 탐색
- 사용자(user): 프로필, 취향 벡터
- 스크랩(scrap): 찜 목록 관리
- 리뷰(review): 사용자 리뷰
- 랭킹(rank): 실시간 급상승 검색어, 디저트 랭킹
- 인프라(infra): Redis 캐싱, 외부 API 연동, S3 스토리지
이걸 계층형으로 짜면 service/ 폴더에 CafeService, UserService, ReviewService, RankService… 가 뒤섞이고, 도메인 간 의존성이 점점 꼬이게 된다.
DDD로 해결한 것
도메인 주도 설계를 채택해서 각 도메인을 독립적인 패키지로 분리했다.
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
src/main/java/com/degging/be/
├── global/ # 전역 설정, 공통 응답, 예외 처리
│ ├── config/
│ ├── dto/
│ ├── exception/
│ └── util/
├── auth/ # 인증 도메인
│ ├── controller/
│ ├── service/
│ ├── repository/
│ ├── entity/
│ └── dto/
├── cafe/ # 카페 도메인
│ ├── controller/
│ ├── service/
│ ├── repository/
│ ├── entity/
│ └── dto/
├── discovery/ # 탐색 도메인
├── user/ # 사용자 도메인
├── scrap/ # 스크랩(찜) 도메인
├── review/ # 리뷰 도메인
├── rank/ # 랭킹 도메인
└── infra/ # 인프라 모듈
├── cache/redis/
├── external/kakao/
└── storage/s3/
각 도메인 패키지 안에는 controller/, service/, repository/, entity/, dto/ 하위 패키지가 동일한 구조로 들어가 있다. 현재는 global 모듈의 코드(공통 응답, 예외 처리)가 먼저 구현된 상태이고, 이후 각 도메인의 비즈니스 로직을 채워나갈 예정이다.
이렇게 구성하면:
- 도메인별 독립성 확보: 카페 도메인 수정이 사용자 도메인에 영향을 주지 않는다
- 팀원 간 협업 용이: “나는 카페 도메인, 너는 사용자 도메인” 식으로 역할을 명확히 나눌 수 있다
- 확장성: 새로운 도메인이 추가되어도 기존 코드를 건드릴 필요 없이 새 패키지만 추가하면 된다
특히 이 프로젝트는 백엔드 2명이 동시에 작업하는 구조였기 때문에 도메인별 분리가 충돌을 줄이는 데 큰 도움이 되었다.
global 모듈의 코드를 가장 먼저 작성한 이유는, 어떤 도메인이든 공통으로 사용하는 응답 포맷과 예외 처리가 먼저 잡혀있어야 이후 각 도메인 개발이 매끄럽게 진행되기 때문이다. 구체적으로 어떻게 공통 응답과 예외 처리를 설계했는지는 다음 포스팅에서 자세히 다루겠다.
다음 편에서는 백엔드의 기본기이자 가장 중요한 Global 모듈 (공통 응답 포맷 및 전역 예외 처리) 설계에 대해 다뤄보겠다.