8장 : 경계 간 매핑하기

계층의 모델을 매핑하는 몇 가지 전략과 장단점을 알아본다.

웹 계층에서 웹 컨트롤러가 Account 객체를 인자로 가지는 SendMoneyUseCase 인터페이스를 호출해서 유스케이스를 실행한다. (두 계층이 같은 모델을 사용)

‘매핑하지 않기’ 전략

  • 단인 책임 원칙 위반

    • 웹 계층과 영속성 계층은 모델에 대해 특별한 요구사항이 있을 수 있다. ex)

    • 도메인과 애플리케이션 계층은 웹이나 영속성과 관련된 특수한 요구사항에 관심이 없음에도 불구하고 Account 도메인 모델 클래스는 이런 모든 요구사항을 다뤄야 함

    • Account 클래스는 웹, 애플리케이션, 영속성 계층과 관련된 이유로 인해 변경돼야 함

    • 기술적인 요구사항이 아니더라도, 각 계층이 Account 클래스에 특정 커스텀 필드를 두도록 요구할 수 있다.

  • 모든 계층이 정확히 같은 구조의, 정확히 같은 정보를 필요로 한다면 ‘매핑하지 않기’ 전략은 완벽한 선택지다.

    • 간단한 CRUD 유스케이스의 경우, 같은 필드를 가진 모델들끼리시 서로 굳이 매핑할 필요가 없다.

    • 그러나 애플리케이션 계층이나 도메인 계층에서 웹과 영속성 문제를 다루게 되면 곧바로 다른 전략을 취해야한다. (애너테이션을 제외하더라도 말이다)

어떤 매핑 전략을 선택했더라도 나중에 언제든 바꿀 수 있다.

웹 계층에서 웹 컨트롤러가 Account 객체를 인자로 가지는 SendMoneyUseCase 인터페이스를 호출해서 유스케이스를 실행한다. (두 계층이 같은 모델을 사용)

‘양방향' 매핑 전략

  • 단인 책임 원칙 위반

    • 웹 계층과 영속성 계층은 모델에 대해 특별한 요구사항이 있을 수 있다. ex)

    • 도메인과 애플리케이션 계층은 웹이나 영속성과 관련된 특수한 요구사항에 관심이 없음에도 불구하고 Account 도메인 모델 클래스는 이런 모든 요구사항을 다뤄야 함

    • Account 클래스는 웹, 애플리케이션, 영속성 계층과 관련된 이유로 인해 변경돼야 함

    • 기술적인 요구사항이 아니더라도, 각 계층이 Account 클래스에 특정 커스텀 필드를 두도록 요구할 수 있다.

  • 모든 계층이 정확히 같은 구조의, 정확히 같은 정보를 필요로 한다면 ‘매핑하지 않기’ 전략은 완벽한 선택지다.

    • 간단한 CRUD 유스케이스의 경우, 같은 필드를 가진 모델들끼리시 서로 굳이 매핑할 필요가 없다.

    • 그러나 애플리케이션 계층이나 도메인 계층에서 웹과 영속성 문제를 다루게 되면 곧바로 다른 전략을 취해야한다. (애너테이션을 제외하더라도 말이다)

각 계층이 전용 모델을 가진 매핑 전략을 ‘양방향’ 매핑 전략이라고 한다.

어떤 매핑 전략을 선택했더라도 나중에 언제든 바꿀 수 있다.

‘양방향' 매핑 전략

  • 각 계층은 도메인 모델과는 완전히 다른 구조의 전용 모델을 가지고 있따.

    • 웹 계층에서는 웹 모델을 인커밍 포트에서 필요한 도메인 모델로 매핑하고, 인커밍 포트에 ㅡ이해 반환된 도메인 객체를 다시 웹 모델로 매핑한다.

    • 영속성 계층은 아웃고잉 포트가 사용하는 도메인 모델과 영속성 모델 간의 매핑과 유사한 매핑을 담당한다.

각 계층이 전용 모델을 가지고 있어, 각 계층의 전용 모델을 변경해도 다른 계층에는 영향이 없다.

따라서

  • 웹 모델은 데이터를 최적으로 표현할 수 있는 구조를 가질 수 있고,

  • 도메인 모델은 유스케이스를 잘 수현할 수 있는 구조를 가질 수 있다.

  • 영속성 모델은 데이터베이스에 객체를 저장하기 위해 ORM에서 필요로 하는 구조를 가질 수 있따.

각 계층이 전용 모델을 가진 매핑 전략을 ‘양방향’ 매핑 전략이라고 한다.

장점

  • 각 계층은 도메인 모델과는 완전히 다른 구조의 전용 모델을 가지고 있따.

    • 웹 계층에서는 웹 모델을 인커밍 포트에서 필요한 도메인 모델로 매핑하고, 인커밍 포트에 ㅡ이해 반환된 도메인 객체를 다시 웹 모델로 매핑한다.

    • 영속성 계층은 아웃고잉 포트가 사용하는 도메인 모델과 영속성 모델 간의 매핑과 유사한 매핑을 담당한다.

  • 웹이나 영속성 관심사로 어염되지 않느 꺠끗한 도메인 모델

  • JSON이나 ORM 매필 애너테리션도 없어짐

  • 단일 책임 원칙 만족

  • 간단

각 계층이 전용 모델을 가지고 있어, 각 계층의 전용 모델을 변경해도 다른 계층에는 영향이 없다.

단점

따라서

  • 너무 많은 보일러 플레이트 코드

  • 도메인 모델이 계층 경계를 넘어서 통신

    • 인커밍 포트와 아웃고잉 포트는 도메인 객체를 입력 파라미터와 반환값으로 사용한다. 도메인 모델은 도메인 모델의 필요에 의해서만 변경되는 것이 이상적이지만 바깥쪽 계층의 요구에 따른 변경에 취약해진다.

  • 웹 모델은 데이터를 최적으로 표현할 수 있는 구조를 가질 수 있고,

  • 도메인 모델은 유스케이스를 잘 수현할 수 있는 구조를 가질 수 있다.

  • 영속성 모델은 데이터베이스에 객체를 저장하기 위해 ORM에서 필요로 하는 구조를 가질 수 있따.

장점

  • 웹이나 영속성 관심사로 어염되지 않느 꺠끗한 도메인 모델

  • JSON이나 ORM 매필 애너테리션도 없어짐

  • 단일 책임 원칙 만족

  • 간단

단점

  • 너무 많은 보일러 플레이트 코드

  • 도메인 모델이 계층 경계를 넘어서 통신

    • 인커밍 포트와 아웃고잉 포트는 도메인 객체를 입력 파라미터와 반환값으로 사용한다. 도메인 모델은 도메인 모델의 필요에 의해서만 변경되는 것이 이상적이지만 바깥쪽 계층의 요구에 따른 변경에 취약해진다.

‘완전’ 매핑 전략

각 연산마다 별도의 입출력 모델을 사용한다.

  • 계층 경계를 넘어 통신할 때 도메인 모델을 사용하는 대신 SendMoneyUseCase 포트의 입력 모델로 동작하는 SendMoneyCommand처러 ㅁ각 작업에 특화된 모데을 사용.

  • 이런 모델을 가리켜 ‘’커맨드(command), ’요청(request)’ 혹은 이와 비슷한 단어로 표현한다.

  • 웹 계층은 입력을 애플리케이션 계층의 커맨드 객체로 매핑할 책임을 가지고 있다. 이러한 커맨드 객체는 애플리케이션 계층의 인터페이스를 해석할 여지 없이 명확하게 만들어준다.

  • 각 유스케이스는 전용 필드와 유효성 검증 로직을 가진 전용 커맨드를 가진다.

  • 그러고 나서 애플리케이션 계층은 커맨드 객체를 유스케이스에 따라 도메인 모델을 변경하기 위해 필요한 무엇인가로 매핑할 팩임을 가진다.

언제

  • 웹 계층과 애플리케이션 계층 사이에서 상태 변경 유스케이스의 경계를 명확하게 할 떄 가장 빛을 발함

  • 케바케 → 상황에 따라

장점

각 연산마다 별도의 입출력 모델을 사용한다.

  • 여러 유스케이스의 요구사항을 함께 다뤄야 하는 매핑에 비해 구현하고 유지보수 용이

  • 계층 경계를 넘어 통신할 때 도메인 모델을 사용하는 대신 SendMoneyUseCase 포트의 입력 모델로 동작하는 SendMoneyCommand처러 ㅁ각 작업에 특화된 모데을 사용.

  • 이런 모델을 가리켜 ‘’커맨드(command), ’요청(request)’ 혹은 이와 비슷한 단어로 표현한다.

  • 웹 계층은 입력을 애플리케이션 계층의 커맨드 객체로 매핑할 책임을 가지고 있다. 이러한 커맨드 객체는 애플리케이션 계층의 인터페이스를 해석할 여지 없이 명확하게 만들어준다.

  • 각 유스케이스는 전용 필드와 유효성 검증 로직을 가진 전용 커맨드를 가진다.

  • 그러고 나서 애플리케이션 계층은 커맨드 객체를 유스케이스에 따라 도메인 모델을 변경하기 위해 필요한 무엇인가로 매핑할 팩임을 가진다.

단점

언제

  • 매핑하는데많은 코드 필요

  • 애플리케이션 계층과 영속성 계층 사이에서는매핑 오버헤드

  • 웹 계층과 애플리케이션 계층 사이에서 상태 변경 유스케이스의 경계를 명확하게 할 떄 가장 빛을 발함

  • 케바케 → 상황에 따라

장점

  • 여러 유스케이스의 요구사항을 함께 다뤄야 하는 매핑에 비해 구현하고 유지보수 용이

단점

  • 매핑하는데많은 코드 필요

  • 애플리케이션 계층과 영속성 계층 사이에서는매핑 오버헤드

‘단방향’ 매핑 전략

  • 모든 계층의 모델들이 같은 인터페이스를 구현한다.

  • 이 인터페이스는 관련 있는 특성(attribute)에 대한 getter 메서드를 제공해서 도메인 모델의 상태를 캡슐화 한다.

도메인 모델 자체는 풍부한 행동을 구혀할 수 있고, 애플리케이션 계층 내의 서비스에서 이러한 해옫ㅇ에 접근할 수 있다. 도메인 객테를 바깥 계층으로 전다랗고 싶으면 매핑 없이 할 수 있는데, 도메인 객체가 인커밍/아웃고잉 포트가 기대하는 대로 상태 인터페이스를 구현하고 있기 때문이다.

DDD 용어인 팩터리는 어떠 ㄴ특정한 상태로부터 도메인 객체를 재구성할 책임을 가지고 있다.

  • 이 전략에서 매핑 책임은 명확하다. 만약 한 계층이 다른 계층으로부터 객체를 받으면 해당 계층에서 이용할 수 있또록 다른 무언가로 매핑하는 것이다. 그러므로 각 계층은 한 방향으로만 매핑한다.

  • 이 전략은 계층 간의 모델이 비슷할 때 가장 효과적이다.

  • 모든 계층의 모델들이 같은 인터페이스를 구현한다.

  • 이 인터페이스는 관련 있는 특성(attribute)에 대한 getter 메서드를 제공해서 도메인 모델의 상태를 캡슐화 한다.

도메인 모델 자체는 풍부한 행동을 구혀할 수 있고, 애플리케이션 계층 내의 서비스에서 이러한 해옫ㅇ에 접근할 수 있다. 도메인 객테를 바깥 계층으로 전다랗고 싶으면 매핑 없이 할 수 있는데, 도메인 객체가 인커밍/아웃고잉 포트가 기대하는 대로 상태 인터페이스를 구현하고 있기 때문이다.

DDD 용어인 팩터리는 어떠 ㄴ특정한 상태로부터 도메인 객체를 재구성할 책임을 가지고 있다.

  • 이 전략에서 매핑 책임은 명확하다. 만약 한 계층이 다른 계층으로부터 객체를 받으면 해당 계층에서 이용할 수 있또록 다른 무언가로 매핑하는 것이다. 그러므로 각 계층은 한 방향으로만 매핑한다.

  • 이 전략은 계층 간의 모델이 비슷할 때 가장 효과적이다.

언제 어떤 매핑 전략을 사용할 것인가 ?

그때그떄 다르다

example

변경 유스케이스를 작업하고 있다면 웹 계층과 애플리케이션 계층 사이에서는 유스케이스 간의 결합을 제거하기 위해 ‘완전 매핑’ 전략을 첫 번째 선택지로 선택해야 한다. 이렇게 하면 유스케이스별 유효성 검증 규칙이 명확해지고 특정 유스케이스에서 필요하지 않은 필드를 다루지 않아도 된다.

변경 유스케이스를 작업하고 있다면 애플리케이션과 영속성 계층 사이에서는 매핑 오버헤드를 줄이고 빠르게 코드를 짜기 위해서 ‘매핑하지 않기’ 전략을 첫 번째 선택지로 둔다. 하지만 애플리케이션 계층에서 영속성 문제를 다뤄야 하게 되면 ‘양방향’ 매핑 전략으로 바꿔서 영속성 문제를 영속성 계층에 가둘 수 있게 한다.

쿼리 작업을 한다면 매필 오버헤드들 줄이고 빠르게 코드를 짜기 위해 ‘매핑지 않기’ 전햑이 웹 계층과 애플리케이션 계층 사이, 애플리케이션 계층과 영속성 계층 사이에서 첫 번째 선택지다 돼야 한다.

DISCUSSION

Last updated