[클린 아키텍처] Ch 08. 경계 간 매핑하기

정미·2022년 5월 18일
0

각 계층의 모델을 매핑하는 방법

논쟁

  1. 매핑에 찬성
    • 양 계층에서 같은 모델을 사용하게 되면 두 계층이 강하게 결합된다.
  2. 매핑에 반대
    • 보일러플레이트 코드: 상용구 코드 또는 단순히 상용구는 거의 또는 전혀 변형 없이 여러 위치에서 반복되는 코드 섹션
    • 많은 유스케이스가 CRUD만 수행하고, 같은 모델을 계층에 걸쳐서 사용한다.

결론

  1. 각 유스케이스마다 적절한 전략을 택할 수 있어야 한다.
  2. 어떤 매핑 전략을 선택했더라도 나중에 언제든 바꿀 수 있다.
  3. 여러 가지 매핑 전략을 섞어쓸 수 있고, 섞어 써야만 한다.

8-1. ‘매핑하지 않기(No Mapping)’ 전략

웹, 애플리케이션, 도메인, 영속성 계층이 모두 같은 모델 사용

Account 도메인 모델

  • 웹 계층: 필드에 모델 ↔ JSON 직렬화, 역직렬화하기 위한 애너테이션 필요 ex> @NotNull, ..
  • 영속성 계층: 데이터베이스 매핑을 위한 애너테이션 ex> @Entity, @Id, @Column, …
  • 애플리케이션, 도메인 계층: 위 요구사항들이 필요없음에도 가지고 있어야 한다.

단점

  • SRP 위반: 여러 계층에 의해 변경될 가능성 있음
  • 특정 계층에서만 필요한 필드 추가할 경우 파편화된 모델이 됨

결론

모든 계층이 정확히 같은 구조, 같은 정보를 필요로 한다면 완벽한 선택지이다.

하지만 웹, 영속성 문제를 조금이라도 다루게 된다면 다른 전략을 취해야 한다.

8-2. ‘양방향(Two-Way)’ 매핑 전략

각 계층이 전용 모델을 가진 매핑 전략

계층별 책임

  • 웹 계층: 웹 모델 ↔ 도메인 모델 매핑
  • 영속성 계층: 영속성 모델 ↔ 도메인 모델 매핑

계층별 모델

  • 웹 모델: 데이터를 최적으로 표현할 수 있는 구조 + JSON annotation
  • 도메인 모델: 유스케이스를 제일 잘 구현할 수 있는 구조
    • 웹, 영속성 관심사로 오염되지않은 깨끗한 도메인 모델. 도메인 로직에 집중할 수 있다.
  • 영속성 모델: 데이터베이스에 객체를 저장하기 위해 ORM에서 필요로 하는 구조 + ORM annotation

장점

  1. 각 계층 전용 모델이 변경되더라도 다른 계층에 영향을 주지 않는다. ← SRP
  2. 간단한 전략. 매핑 책임이 명확하다.
    • 바깥쪽 계층의 어댑터가 안쪽 계층의 모델로 매핑, 역방향 매핑

단점

  1. 너무 많은 보일러플레이트 코드
    • 매핑 프레임워크가 제네릭 코드와 리플렉션 뒤로 내부 동작 방식을 숨길 경우 디버깅이 매우 어렵다.
  2. 도메인 모델이 계층 경계를 넘어 통신하는데 사용된다.
    • = 인커밍, 아웃고잉 포트의 파라미터와 리턴값으로 사용된다.
    • 도메인 모델의 필요가 아닌 바깥쪽 계층의 요구에 의해서 변경될 수도 있다.

결론

해당 전략도 어떤 엔지니어링 상황에서도 완벽하게 잘 들어맞는 해결책은 아니다.

8-3. ‘완전(Full)’ 매핑 전략

각 연산마다 별도의 입출력 모델

  • 계층 경계를 넘어 통신할 때 도메인 모델 대신 SendMoneyCommand(인커밍 포트의 파라미터) 같은 각 작업에 특화된 모델을 사용한다.

계층별 책임

  • 웹 계층: 입력 → 애플리케이션 계층의 커맨드(요청) 객체로 매핑
    • 각 유스케이스는 전용 필드와 유효성 검증 로직을 가진 전용 커맨드를 가진다.
  • 애플리케이션 계층: 커맨드 객체 → 도메인 모델

장단점

  • 웹 모델 : 도메인 모델 = 1 : 1 일 경우 보다 여러 개의 커맨드로 매핑할 때 더 많은 코드가 필요
  • 여러 유스케이스의 요구사항을 함께 다룰 때 구현, 유지보수가 쉽다.

결론

전역 패턴으로 추천하지는 않는다.

인커밍 어댑터와 애플리케이션 계층 사이에서 상태 변경 유스케이스의 경계를 명확하게 할 때 좋다.

애플리케이션 계층과 영속성 계층 사이에서는 매핑 오버헤드 때문에 사용하지 않는 게 좋다.

8-4. ‘단방향 매핑(One-Way)’ 전략

모든 계층들이 동일한 ‘상태’ 인터페이스를 구현

이때 다른 계층에서 온 객체를 단방향으로 매핑하기만 하면 된다.

상태 인터페이스

도메인 모델의 상태를 캡슐화

관련 있는 특성(attribute)에 대한 getter 제공

장점

  1. 도메인 모델 자체는 풍부한 행동을 구현할 수 있고, 서비스에서 여기에 접근할 수 있다.
  2. 인커밍/아웃고잉 포트는 상태 인터페이스를 가지고 있기 때문에 계층의 전용 모델(구현체)을 매핑 없이 다른 계층으로 전달할 수 있다.
    • 받은 계층에서 상태 인터페이스를 이용할지, 전용 모델로 매핑할지 결정
    • DDD의 factory: 어떤 특정한 상태로부터 도메인 객체를 재구성할 책임
  3. 매핑 책임이 명확하다.
    • 한 계층이 다른 계층으로부터 객체를 받으면 해당 계층에서 이용할 수 있도록 전용 모델로 매핑 = 한 방향으로 매핑

단점

  1. 매핑이 여러 계층에 퍼져 있기 때문에 다른 전략에 비해 어렵다.

결론

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

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

그때그때 다르다

  • 한 전략을 전체 코드에 적용하려고해선 안 된다.
  • 빠르게 코드를 짤 수 있는 간단한 전략으로 시작해서 계층 간 결합을 떼어내는 데 도움이 되는 복잡한 전략으로 갈아타는 방법
  • 팀 내 가이드라인 정하기: 어떤 상황에서 어떤 매핑 전략을 가장 먼저 택해야 하는가?

전략 선택 예시

  1. 변경 유스케이스
    • 웹~애플리케이션 계층
      1. 유스케이스간 결합 제거하기 위해 ‘완전 매핑’ 전략 선택
        • 유스케이스별 유효성 검증규칙이 명확해진다.
        • 특정 유스케이스에 필요하지 않은 필드를 다루지 않아도 된다.
    • 애플리케이션~영속성 계층
      1. 매핑 오버헤드를 줄이고 빠른 코딩을 위해 ‘매핑하지 않기’ 전략 선택
      2. 애플리케이션 계층에서 영속성 문제를 다루게 된다면 ‘양방향’ 매핑’ 전략으로 영속성 문제를 영속성 계층에 가둔다.
  2. 쿼리 유스케이스
    1. 모든 계층간 매핑에서 ‘매핑하지 않기’ 전략을 첫번째로 선택
    2. 애플리케이션 계층에서 웹/영속성 문제를 다루어야 한다면 각 계층 사이에서 ‘양방향’ 매핑 전략으로 변경

8-6. 유지보수 가능한 소프트웨어를 만드는 데 어떻게 도움이 될까?

인커밍/아웃고잉 포트

  • 서로 다른 계층이 어떻게 통신해야 하는지 정의
  • 계층 사이에 매핑을 수행할지 여부, 어떤 매핑 전략을 선택할지
    • 전용 모델을 사용하느냐, 통일된 모델을 넘겨주느냐

유스케이스별 특화된 좁은 포트를 사용하면 서로 영향을 끼치지 않으면서 유스케이스마다 다른 최선의 매핑 전략을 사용할 수 있다.

더 어렵고 많은 커뮤니케이션을 필요로 하지만 가이드라인이 있으면 더 유지보수하기 쉬운 코드가 될 것이다.


질문 & 논의할 점

  1. 7-3 완전 매핑 전략 p103 / 웹 계층과 애플리케이션 계층 사이 상태 변경 유스케이스의 경계를 명확하게 할 때 가장 빛을 발한다. 애플리케이션 계층과 영속성 계층 사이에서는 매핑 오버헤드 때문에 사용하지 않는 것이 좋다.

    • 왜 앱-영속성 계층 사이만 매핑 오버헤드이지?
    1. 웹 계층이 애플리케이션 계층 인커밍 포트의 메서드를 호출하는 입장이다. 따라서 이 메서드를 호출하면서 파라미터로 넘겨줄 커맨드 객체를 웹 계층에서 매핑할 책임이 있다. 반면 아웃고잉 포트에서 별도의 전용 객체를 사용한다면, 이 아웃고잉 포트를 호출하는 서비스에서 도메인 모델을 이 전용 객체로 매핑해야할 책임이 있다. 따라서 애플리케이션 코어에서 매핑해야하는 이 상황을 매핑 오버헤드라고 해석했다.
    2. 각 유스케이스는 필요한 필드들과 검증이 다르기 때문에 연산별 전용 모델이 따로 필요하다.(웹 ↔ 애플리케이션) 하지만 한 도메인 내에서 연산들은 같은 영속성 모델 엔티티를 사용한다.(애플리케이션 ↔ 영속성) 이때 아웃고잉 포트에서 각자 다른 전용 모델을 사용한다면 한 엔티티로 어차피 변환해야하기 때문에 매핑 오버헤드이다.
  2. 7-4 단방향 매핑 전략 p104 마지막 문단 / 읽기 전용 연산은 인터페이스가 필요한 모든 정보를 제공하기 때문에 웹 계층에서 전용 모델로 매핑할 필요가 전혀 없다.

    → 이럴 때에는 매핑하지 않기 전략을 사용하라는 뜻?

  3. 7-5 매핑 전략 선택 p106 첫 문단 / 변경 유스케이스, 애플리케이션과 영속성 계층 사이에서 매핑하지 않기 전략을 첫 번째 선택지로 둔다. 하지만 애플리케이션 계층에서 영속성 문제를 다뤄야 되면 ‘양방향’ 매핑 전략으로 바꾼다.

    • 그러면 @Entity, @Id 등 애노테이션을 가진 하나의 모델을 애플리케이션 계층, 아웃고잉 포트, 아웃고잉 어댑터에서 사용하라는 건데 이 자체가 애플리케이션 계층에서 영속성 문제를 가지게 되는것 아닌가

0개의 댓글