카카오엔터테인먼트가 레거시 서버를 MSA 로 포팅하며 Domain Driven Design 을 적용한 사례다. TDD, BDD, DDD 를 비교해 DDD 를 고르고, Hexagonal Architecture 로 구현했다. 얻은 것과 함께 Mapper 코드량 증가 같은 대가도 솔직하게 적는다.
핵심 포인트- DDD 를 고른 이유는 두 가지다. 완전히 새로운 시스템이 아니라 기존 시스템의 분해와 개선이 가능하고, 비즈니스와 개발 사이의 보편적 이해와 전체 그림을 맞출 수 있어서다.
- 테스트 케이스를 하나하나 새로 만드는 대신 기존 로직을 최대한 살리면서, 레거시 서버와 신규 MSA 서버가 동시에 동작하는 점진적 교체를 택했다.
- 핵심 개념 셋을 든다. 범위를 구분한 하위 도메인인 Bounded Context, 그 사이 관계를 보여주는 Context Map, 데이터 변경 단위이자 라이프사이클이 같은 도메인의 집합인 Aggregate 다.
- 구현은 Hexagonal Architecture 다. 전통적 Layered 는 비즈니스 로직이 거대해지며 오염되는 게 염려돼 제외했고, 포트와 어댑터라는 명확한 구현 개념이 있어 골랐다.
- 핵심 원칙은 비즈니스 로직이 표현 로직이나 데이터 접근 로직에 의존하지 않는 것이다.
상세 정리- 파트너사이트의 기능 영역들을 분류해 콘텐츠 제작과 메타정보 관리, 판매를 하나의 경계로 묶었고 그것이 MSA 상 하나의 서비스가 된다.
- 분류된 Context 들은 서로의 도메인을 철저히 분리하고 API 를 통해 CRUD 를 수행한다.
- Context Map 은 컨텍스트 간 업스트림과 다운스트림을 한눈에 보여줘 전체 흐름을 예상하게 해준다.
- 서비스가 가진 객체를 그대로 쓰는 것은 의미가 없어, 각 도메인 영역을 대표하는 도메인 객체 집합을 따로 설계했다.
- Aggregate 는 Root Entity 를 통해서만 접근하게 하고, 그 접근으로 인한 변경이 내부 모든 객체에 영향을 미치도록 한다.
- 그렇게 하면 내부 개별 객체의 상호작용보다 다른 Aggregate 나 객체와의 관계를 넓은 시야로 볼 수 있고, 제약사항을 하나의 맥락으로 관리하게 된다.
- 포트와 어댑터는 게임기의 HDMI 와 USB 포트에 비유한다. 맞는 포트와 어댑터를 연결하면 어떤 식으로든 쓸 수 있다는 것이다.
- 샘플 도메인은 Product 다. 판매 정보와 메타 정보, 콘텐츠 파일 정보를 담고 판매 시작 시 유효성 검사와 시작 날짜 처리를 도메인 안에 둔다.
- 흐름은 Controller 가 Use Case 에 정의된 작품 판매를 호출하고, 구현은 SaleService 에, 실제 DB 통신은 Load 와 Save 포트에서 이뤄지는 식이다.
- 같은 DB CRUD 인데 Load 와 Save 포트를 나눈 것은 조회 성능 개선을 염두에 뒀고, 나중에 SQS 나 RabbitMQ 로 이벤트 기반 전환을 예정하고 있어서다.
- Adapter 에는 Mapper 가 필요하다. DDD 는 저장된 데이터를 그대로 쓰지 않고 도메인으로 변환해 쓰므로 JpaEntity 와 DomainEntity 사이 양방향 변환이 있어야 한다.
- 대가도 적는다. MSA 자체의 개발 복잡도와 숙련도, 트랜잭션 관리, 배포 복잡도에 더해 Mapper 처럼 Hexagonal 구현에서 생기는 코드량이 생각보다 많았다.
- 각 도메인에 대한 높은 이해가 필요해, 신규 도메인이나 기존 도메인 수정 시 설계 검토를 충분히 거쳐야 구현에 들어갈 수 있게 됐다.
- 얻은 것은 도메인 간 관계가 복잡해도 큰 틀에서 정리가 되고, 데이터를 도메인으로 한 번 정제해 결합도가 낮고 응집도가 높아졌다는 점이다.
- 개발자 입장에서는 Aggregate 로 도메인이 캡슐화되고 Use Case 와 Port 로 의존도가 낮아져 비즈니스 로직에 집중하게 됐고 가독성도 올랐다고 정리한다.
왜 읽나레거시를 MSA 로 쪼개며 설계 방법론을 고르는 팀에게, DDD 와 Hexagonal 을 실제로 적용해 얻은 것과 치른 비용을 함께 보여주는 사례.