08. 경계 간 매핑하기
- 웹, 애플리케이션, 도메인, 영속성 계층이 존재
- 각 계층의 모델을 매핑하는 것에 대한 논의가 필요
- 각 계층이 공통된 모델을 사용하면 매핑을 위한 매퍼 클래스를 구현할 필요가 없다.
- 매핑에 찬성하는 개발자
- 매핑 없이 양 계층이 동일한 모델을 사용하면 두 계층이 강하게 결합하게 된다.
- 매핑에 반대하는 개발자
- 매핑을 하게 되면 보일러플레이트 코드를 너무 많이 만들게 된다.
- CURD 중심의 애플리케이션에서 계층 사이의 매핑은 과하다.
‘매핑하지 않기’ 전략
- 위 관계에서 웹, 애플리케이션, 영속성 계층이 전부
Account
객체를 모델로 사용한다.
- 각 계층에서 특별한 요구사항이 생길 수 있다.
- ex) 웹 계층에서 JSON을 직렬화하기 위한 애너테이션이 필요
- ex) 영속성 계층에서 ORM 프레임워크를 위한 애너테이션이 필요
- 각 계층의 요구사항을 한 모델에 반영하게 되면 여러 계층의 변경 사항이 한 모델에 영향을 미치므로 단일 책임 원칙을 위반하게 된다.
- 다만 단순한 CRUD 유스케이스에서 모든 계층의 모델이 정확히 같은 구조, 정보를 필요로 한다면 ‘매핑하지 않기’는 좋은 선택이다.
- 어떤 매핑 전략을 선택하더라도 나중에 언제든 바뀔 수도 있다.
‘양방향’ 매핑 전락
- 각 계층은 도메인 모델과는 완전 다른 구조의 전용 모델을 가진다.
- 웹 계층에선 인커밍 포트에서 필요한 도메인 모델로 매핑하고, 인커밍 포트에 의해 반환된 도메인 객체를 다시 웹 모델로 매핑한다.
- 영속성 계층은 아웃고잉 포트가 사용하는 도메인 모델과 영속성 모델 간 매핑을 담당
- 각 계층이 전용 모델을 변경하더라도 다른 계층에는 영향이 없다. (내용이 변경되지 않는 한)
- ‘양방향’ 매핑은 매핑 책임이 명확하다는 장점이 있다.
- 바깥쪽 계층/어댑터(웹, 영속성 계층)는 안쪽 계층의 모델로 매핑하고 다시 반대로 매핑
- 안쪽 계층은 해당 계층의 모델만 알면 되고 매핑 대신 도메인 로직에 집중할 수 있다.
- ‘양방향’ 매핑의 단점
- 너무 많은 보일러플레이트 코드가 생긴다.
- 구현하는 데 비용이 꽤 든다.
- 매핑 프레임워크를 사용할 경우 매핑 로직 디버깅이 힘들어질 수도 있다. (제네릭, 리플렉션 때문)
- 도메인 모델이 계층 경계를 넘어 통신하는 데 사용된다.
- 인커밍, 아웃고잉 포트는 도메인 객체를 파라미터와 반환값으로 사용
- 바깥쪽 계층 요구에 따른 변경에 취약하다는 것
- 어떤 매핑 전략도 은총알은 아니기에 각 유스케이스마다 적절한 전략을 택할 수 있어야 한다.
‘완전’ 매핑 전략
- ‘완전’ 매핑 전략에서는 각 연산마다 별도의 입출력 모델을 사용한다.
- 계층 간 통신할 때 도메인 모델 대신 각 작업에 특화된 모델을 사용하는 것
- 각 유스케이스는 전용 필드와 유효성 검증 로직을 가진 전용 커맨드를 가진다.
- ex)
SendMoneyUseCase
에서 SendMoneyCommand
를 파라미터로 사용
- 커맨드 객체를 사용하면 어떤 필드를 채울지, 비워둘지 추측할 필요가 전혀 없다. (유스케이스마다의 전용 커맨드 객체이기에)
- 각 연산마다 커맨드 객체를 매핑하려면 더 많은 코드가 필요하지만 여러 유스케이스 요구사항을 함께 다뤄야 하는 매핑에 비해 구현하고 유지보수하기가 훨씬 쉽다.
- ‘완전’ 매핑 전략은 전역 패턴으로 추천하지는 않는다.
- 웹 ↔ 애플리케이션 계층 사이에서 가장 효과가 좋다.
- 영속성 계층과는 매핑 오버헤드 때문에 사용하지 않는 것이 좋다.
- 매핑 전략은 모든 계층에 걸쳐 전역 규칙일 필요가 없고 섞어 써야만 한다.
‘단방향’ 매핑 전략
- 이 전략에서 모든 계층 모델들이 같은 인터페이스를 구현한다.
- 이 인터페이스는 관련 있는 특성(attribute)에 대한 getter 메서드를 제공해 도메인 모델 상태를 캡슐화한다.
- 도메인 모델 자체는 풍부한 행동을 구현할 수 있고 바깥 계층에 전달하고 싶으면 매핑 없이 할 수 있다.
- 바깥 계층에선 전달 받은 상태 인터페이스를 매핑할지 안할지 결정할 수 있다.
- 인터페이스로 전달 받았기 때문에 도메인 객체 상태를 변경하는 일은 발생하지 않는다.
- 이 매핑은 팩터리(factory)라는 DDD 개념과 잘 어울린다.
- 매핑이 계층을 넘나들며 퍼져 있기에 다른 전략에 비해 개념적으로 어렵다.
- 이 전략은 계층 간 모델이 비슷할 때 효과적이다.
언제 어떤 매핑 전략을 사용할 것인가
- 각 매핑 전략이 장단점이 존재하기에 특정 전략을 전역 규칙으로 정의하면 안 된다.
- 소프트웨어는 시간이 지나며 변화하기에 전략을 변경하는 것도 방법이다.
- 팀 내에서 합의할 수 있는 가이드라인을 정해 전략을 선택해야 한다.
가이드라인 예시
- 변경 유스케이스를 작업할 때
- 웹 ↔ 애플리케이션 계층 사이엔 결합을 제거하기 위해 ‘완전 매핑’을 먼저 선택
- 애플리케이션 ↔ 영속성 계층 사이에선 ‘매핑하지 않기’ 전략을 첫 번째로 택한다. (생산성 위해)
- 단 애플리케이션 계층에서 영속성 문제를 다뤄야 하면 ‘양방향’ 매핑 전략으로 변경
- 쿼리 작업할 때
- 매핑 오버헤드를 줄이고 빠르게 코드를 짜기 위해 ‘매핑하지 않기’ 전략을 웹 ↔ 애플리케이션 계층 ↔ 영속성 계층에서 첫 번째로 택한다.
- 단 애플리케이션 계층에서 다른 계층 문제를 다뤄야 한다면 ‘양방향’ 전략으로 변경