- 일전의 공부한 내용을 실제로 구현해보면서 공부해보고자 시작한 프로젝트다.
- 이전 글
- 아직 공부 중인 개념입니다. 조금 틀려도 너른 이해 부탁드립니다!
3. DDD에 대한 고민
기본적으로 알고 있는 DDD에 대한 내용은 아래와 같다.
- Eric Evans가 주장했다.
- 도메인을 이해하고 모델링하는 것에 목적이 있다.
- 유비 쿼터스 언어로 소통한다. 이를 통해서 기술 도메인과 비즈니스 도메인, 그러니까 개발자와 여러 이해 관계자들 간의 의사소통을 목적으로 한다.
사실 이런 부분 이외에도 정체성으로 구별되며 자체 상태와 동작을 유지하는 엔티티, 엔티티의 표현력을 높여주는 값 객체, 트랜잭션 일관성 경게하는 애그리거트, 도메인 로직만으로는 표현할 수 없는 작업을 나타내는 도메인 서비스, 해결하고자 하는 문제 영역인 도메인 등의 개념이 있다.
- 대략적으로 정리하면
[BoundedContext]
├─ [Aggregate]
│ ├─ Root Entity
│ ├─ Entity
│ ├─ Value Objects
│ └─ DomainEvent
└─ [DomainService]
위와 같은 형태가 되지 않을까 싶다.
여기서 고민이 됐던 점은 엔티티와 도메인 서비스였다.
1.1. 엔티티? JPA에서 말하는 엔티티와 같은 것인가?
DDD에서 말하는 엔티티의 정의는 아래와 같다.
- 엔티티는 식별자를 갖는다.
- 도메인 객체로서의 책임을 가지고, 도메인 이벤트 발생, 상태 변화 등을 처리한다.
- 비즈니스 도메인을 모델링하는 객체로, 도메인 로직과 비즈니스 규칙을 구현한다.
여기에서 말하는 속성들이 꽤나 JPA에서의 엔티티와 헷갈렸다. 아니, 처음에는 동일하게 생각했다. 그도 그럴 것이 식별자를 갖는 것도 있었고, ‘풍부한 엔티티 객체’에서 표방하는 것들이 꽤나 DDD에서의 개념과 유사**했기 때문이다.
그러나 의문을 갖기 시작한 것은 ‘도메인은 특정 기술에 종속되지 않는 것’, ‘순수한 것’이라는 부분이 매우 강조됐었고, 더 나아가 의도적으로 세부 사항에 대한 결정을 미루기 위해서 노력하는 점에서 전혀 말이 맞지 않았기 때문이다.
1.2. 도메인 개념 중심 vs. ORM 매핑 목적 중심
DDD에서는 도메인 모델링에 따라서 문제를 해결하기 위한 객체, 주요 로직, 정책, 동작 등을 알맞게 책임 지우는 것에 관심이 있다. 반대로 JPA의 엔티티는 DB의 모델링 명세이며 DB-OOP 간 패러다임 불일치를 해소하기 위해 만들어진 명세로 테이블을 표현하는 것에 초점이 맞춰져 있다..
결론적으로 두 Entity 모두 객체에 맞는 책임을 지우고 스스로 판단할 수 있게 하는 궤는 같을 수 있다. 그러나 서로가 목표로 하는 방향성이 다르다.
이러한 지점은 개발 시에 도메인이 성숙하지 않은 경우에는 서로를 복제해 둔 것과 같은 모습일 수 있지만 시간이 지남에 따라 양태가 달라질 수 있음을 암시한다.
1.3 그래서 분리 해야 해?
의견차가 실제로 있는 논제로 알고 있다.
실제로 이 둘을 실용적인 관점에서 같은 것이라고 봐도 된다는 의견도 있고, 그래도 다르기 때문에 나눠야 한다는 입장이 있다.
혹은 이 두 관점을 섞어서 사용한다는 의견도 있다. 간단한 CRUD는 합쳐서, 복잡한 경우는 이 둘의 개념을 분리해서 말이다.
개인적으로 분리하는 것이 맞다고 생각한다.
- Domain layer의 순수성을 앗아간다. 물론 JPA가 Jakarta EE의 일부로 정의된 명세다. 그러나 실제 사용은 별도의 구현체에 의존해야 한다. 이는 도메인 계층의 독립성을 저해시킨다.
- 관심사가 다르다. DDD의 Entity는 도메인 문제 해결의 주체이며, JPA Entity는 DB 매핑을 위한 기술적 수단이다. 이 둘을 혼용하게 되면 도메인 주도 설계의 의도가 흐려질 수 있다. 더 나아가 DDD Entity의 문제 해결을 위한 자유로운 확장 시 발생하는 충돌을 미연에 방지할 수 있다.
- JPA를 능동적으로 이용할 수 없는가? JPA와 관련해서 DirtyChecking에 대한 문제 제기가 있다. DomainEntity는 영속성 컨텍스트 관리 대상이 아니어서 직접적으로 이용이 불가하지만 아예 사용이 불가하지는 않다. Domain Entity의 변경 내용을 바탕으로 JPA Entity를 업데이트하는 방향으로는 사용할 수 있다. 이 부분은 ‘무조건 안된다’가 아닌 ‘트레이드 오프가 있고 감수할만 하다.’로 받아들일 수 있다.
2. 도메인 서비스! 어떤 것들이 있을까?
막연하게 ‘그 자체로 도메인 개념이지만 엔티티나 값 객체에 자연스럽게 들어맞지 않는 작업’ 것을 받아들이기에는 예시가 필요했다. 찾아보니와 같았다.
- 결제 요청 유효성 검증 (결제 가능 여부, 한도, 카드의 상태 등)
- 프로모션 할인 조건에 따른 할인 적용(상품, 할인 정책 등)
위와 같이 하나의 엔티티로는 표현하기 어려운 정책 수준의 것들로 파악할 수 있었다. 이외 검증, 계산 등 애매하게 여러군데 걸쳐 있거나 엔티티 혼자서 표현하기 어려운 것들을 구현해둔 것이라고 보였다.
심화해서는 도메인 서비스를 구성하면서 다형성을 통해서 융통성 있는 확장에 대한 상상도 할 수 있었다. 뿐만 아니라 useCase-layer에서 서비스들을 사용해서 애플리케이션 수준의 로직을 그릴 수 있겠다는 그림이 그려졌다.
3. 마치며
책으로 공부하고 지나가는 것이 아니라 직접 해보고 고민해보는 시간을 갖기 위해서 시작했다. 덕분에 위와 같은 고민을 해볼 기회를 가질 수 있게 됐다. 이러한 고민들에 대해서 가장 올바르고 정석적인 방향으로 진행해 보고 싶었다. 그 과정에서 고민을 진행하면서 주변 동료 개발자분들께도 여쭤보기도 했고 일리 있는 다른 의견을 들어보기도 했다. 누군가 해주셨던 “정답은 없다. 다만 선택에 대한 근거가 있으면 된다.” 라는 말씀처럼 누군가 같은 질문을 했을 때 나만의 근거를 들어서 선택지의 폭을 넓혀드릴 수 있는 방향으로 되고 싶다.
추가로 확인해보니 JPA도 DDD를 고려하여 설계된 것으로 보인다.
