어떤 개념인가요 ?
도메인 객체와 DB 엔티티를 별도의 클래스로 분리해서 관리하는 설계 방식이다.
도메인 객체란?
비즈니스 규칙과 행위를 담고 있는 객체라고 생각한다. 즉, 이 시스템이 무엇을 하는가에 대해서 표현해준다.
DB 엔티티란?
DB 테이블과 매핑되는 객체이다. 즉, 데이터를 어떻게 저장하는가를 표현한다.
도메인에서의 객체와 DB에 저장되는 엔티티를 다른 클래스로 두고, 그 사이를 매퍼가 변환해주는 구조로 만든다.
Controller → Domain → Mapper -> OrderEntity → DB
어떤 문제를 해결하려고 나왔나?
핵심의 문제는 관심사가 다른 두 영역의 결합을 끊는 것이다.
JPA로 생각을 해볼 수 있을거 같다.
JPA에서 Entity를 도메인 객체로 그대로 쓴다면 다음과 같은 문제점들이 생긴다.
1. 영속성 기술이 도메인을 침범한다.
- Entity, Column, ManyToOne 같은 JPA 어노테이션이 도메인 클래스에 박힌다.
- 기본 생성자를 강제당해서
불변성을 지키기 어렵다. - 도메인이 JPA(구체적인 특정 ORM 기술)에 종속된다.
2. DB 스키마가 도메인 설계를 주도하게 된다.
- 정규화 규칙 때문에 도메인을 쪼개야 하거나, 반대로 도메인 개념인데 테이블이 아니라서 표현이 어려워진다.
- DB의 지식들과 사정(외래키, 조인 등등)들이 도메인 모델 형태를 결정한다.
3. 변경에 대한 전파가 양방향으로 일어나게 된다.
- 비즈니스 규칙의 변경이 일어나면 → DB 스키마에 영향이 간다.
- DB 스키마가 변경되면 → 도메인의 코드도 영향을 받는다.
- 둘중 하나만 바꾸고 싶어도 같이 바뀐다.
4. Lazy Loading, 영속성 컨텍스트 같은 부수 호과
- 도메인 객체를 다루는 트랜잭션 밖에서 LazyInitializationException이 발생한다.
- 순수 비즈니스 로직이 인프라 동작에 의존하게 된다.
어떻게 동작하나? (큰 그림)
그림 그려보기 !
Controller → Service → Domain 객체 → Repository 구현체 → JPA 엔티티 → Database
- Repository 인터페이스는 도메인 패키지에 있고, 도메인 객체만 주고 받는다.
- Repository 구현체는 인프라 패키지에서 JPA Entity로 변환해 저장한다.
- Mapper가 두 모델 사이를 변역한다.
- 도메인 객체는 JPA를 모른다. → 의존성 역전
언제 쓰고, 언제 안 쓰나?
쓸 때 (도메인과 DB 엔티티 분리가 정당화 되는 상황)
도메인의 복잡도가 높은 상황
상태 전이, 불변식, 정책 등이 많아서 비즈니스 로직 자체가 두꺼울 때, 도메인 클래스에 행위를 많이 담아야 하는데 JPA 제약 때문에 불편할 때,
DB 스키마와 도메인이 1:1로 매핑되지 않을 때
한 도메인 개념이 여러 테이블에 흩어져 있거나, 반대로 여러 도메인 개념이 한 테이블에 섞여 있을 때
저장소가 다양할 때
RDB 뿐만 아니라 Redis, MongoDB, 외부 API, 파일 등 여러 저장소를 같이 쓸 때
변경 빈도와 방향이 다를 때
비즈니스 규칙은 자주 바뀌는데 DB 스키마는 안정적이거나, 반대 상황, 변경 축을 분리하고 싶을 때
테스트 용이성을 중요하게 생각할 때
도메인 로직을 DB 없이 순수 단위 테스트로 빠르게 검증하고 싶을 때
DDD를 본격적으로 적용할 때
안 쓸 때 (분리하지 않아도 되는 상황)
CRUD 위주 단순 애플리케이션
도메인 로직이 거의 없고 데이터를 받아서 저장하고 보여주는 수준일 때
도메인과 스키마가 항상 1:1이고 변경 압력이 적을 때
트로토타입/MVP 단계
검증이 먼저이고 설계는 나중에 하고 싶을 때?
남에게 설명한다면 어떻게 설명할 것인가?
도메인 객체와 DB 엔티티 분리는
비즈니스 규칙과 데이터 저장 방식은 서로 다른 속도로 변하는 관심사 이기 때문에,
다른 클래스로 분리하자는 말입니다.
JPA의 기술처럼 DB 엔티티로 두게 된다면 편하게 개발을 진행할 수 있고, 실제로 그 목적으로 만든 것이기는 합니다. 하지만, 그렇게 된다면 도메인 객체는 JPA 인프라에 대한 사정에 끌려다니게 됩니다. 즉, 도메인의 주인이 비즈니스가 아니가 ORM JPA가 되게 된다고 생각합니다.
그래서 도메인은 순수 java POJO 자바 객체로 두고, JPA Entity는 따로 만들어서 Mapper로 변환을 진행하고, 도메인을 DB를 모르게하고, DB는 도메인에 대한 규칙을 모르게 관심사를 분리하는게 적절하다고 생각합니다.
하지만, 이렇게 도메인과 DB 분리가 필요할 때, 분리하는게 정답이라고 생각하는데요, 단순 CRUD처럼 도메인 로직이 거의 없는 경우라면 분리를 하지 않아도 된다고 생각하지만, 도메인의 복잡도가 높다고 판단되고, 비즈니스 규칙과 스키마의 변경에 대한 주기가 다를 때, 분리의 효과가 더 크다고 생각합니다.
판단 기준을 정리하자면 JPA 어노테이션 없이 도메인 객체를 설계할 때 더 자연스러운가에 대한 질문에 맞다는 판단이 든다면 분리하는게 맞는거 같습니다.