김동형수 개발기

만들면서 배우는 클린 아키텍처 - 08 정리 본문

책 스터디/[완료] 만들면서 배우는 클린아키텍처

만들면서 배우는 클린 아키텍처 - 08 정리

김동형수 2022. 4. 15. 00:38

경계 간 매핑하기

매핑에 관한 논쟁

매핑에 찬성하는 개발자 : 두 계층 간에 매핑을 하지 않으면 양 계층에서 같은 모델을 사용해야 하는데 이렇게 하면 두 계층이 강하게 결합된다.

매핑에 반대하는 개발자 : 두 계층간에 매핑을 하게 되면 보일러플레이트 코드를 너무 많이 만들게 된다. 많은 유스케이스들이 오직 CRUD만 수행하고 계층에 걸쳐 모델을 사용하기 때문에 계층 사이의 매핑은 과하다.

 

매핑을 결정하는데 도움이 되도록 매핑 전략의 장단점을 살펴본다.


'매핑하지 않기' 전략

웹 컨트롤러에서 SendMoneyUseCase 인터페이스 호출해서 유스케이스 실행

인자로 Account 객체를 갖는다. 웹, 애플리케이션 계층 모두에서 Account 클래스에 접근해야한다.

영속성 계층과 애플리케이션 계층도 같은 관계이다. 모두 Account 클래스에 접근해서 사용한다.

 

계층별 모델 클래스에 특별한 요구사항이 있을 수 있다.

웹 계층에서 JSON 직렬화를 위한 어노테이션을 특정 필드에 추가할 수 있다.

ORM 프레임워크를 사용한다면 데이터베이스 매핑을 위한 특정 어노테이션이 필요할 수 있다.

 

도메인과 애플리케이션 계층은 웹이나 영속성과 관련된 특수한 요구사항에 관심이 없어도 모든 계층에서 같은 Account 클래스를 사용하기 때문에 Account 클래스에서 모든 요구사항을 다뤄야한다. 이는 단일 책임 원칙을 위반한다.

 

모든 계층이 동일한 정보(모델)을 가지고 있어야 하는 경우 '매핑하지 않기' 전략이 완벽한 선택지가 된다.

 

저자의 경험에 의하면 시간이 흐르면서 간단한 CRUD 유스케이스가 값비싼 매핑전략이 필요한 비즈니스 유스케이스로 바뀌어갈 수 있다.


'양방향' 매핑 전략

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

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

 

두 계층 모두 양방향으로 매핑하기 때문에 '양방향' 매핑이라고 부른다.

 

장점

  • 각 계층이 전용 모델을 가지고 있다
  • 각 계층이 전용 모델을 변경하더라도 다른 계층에 영향이 없다
  • 웹 모델 데이터 표현에 최적화
  • 도메인 모델은 유스케이스를 잘 구현할 수 있는 구조
  • 영속성 모델은 데이터베이스에 객체를 저장하기 위해 ORM이 필요로 하는 구조
  • 단일 책임 원칙을 만족한다
  • 매핑하지 않기 전략 다음으로 간단한 전략

단점

  • 너무 많은 보일러 플레이트 코드 생성
  • 프레임워크의 도움을 받아도 매핑코드 구현하는데 시간이 걸린다
  • 프레임워크가 제네릭 코드와 리플렉션 뒤로 숨길 경우 디버깅에 어려움
  • 도메인 모델이 계층 경계를 넘어 통신하는데 사용
    • 인커밍 포트와 아웃고잉 포트는 도메인 객체를 입력 파라미터와 반환값으로 사용한다
  • 도메인 모델이 바깥 계층의 요구에 따른 변경에 취약

완벽하게 들어맏는 매핑 전략은 없다. '양방향' 매핑도 예외는 아니다. 이를 무조건 지켜야하는 법칙으로 여긴다면 상황에 따라서 개발 진행속도를 더디게 만든다.

 


'완전' 매핑 전략

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

포트에 입력 모델로 커멘드(command), 요청(request) 혹은 이와 비슷한 단어로 표현한다.

 

웹 계층은 입력을 애플리케이션 계층의 커맨드 객체로 매핑할 책임이 있다.

애플리케이션 계층의 인터페이스에 대한 해석을 명확하게 해준다.

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

이전에 웹 컨트롤러에서 커스텀 벨리데이터로 처리했던 내용들이 있었는데, 만약 헥사고날 아키텍처를 적용하게 된다면 웹 계층에서 1회 유스케이스에서 2회 2번의 유효성 검사를 진행하게 된다.
컨트롤러 - 서비스 1:1이 무조건 적인 원칙이라면 유스케이스에서 1회만 진행해도 될 것 같지만, 코드를 작은단위까지 분리하게 된다면 컨트롤러와 유스케이스에서 각각 입력 유효성검증을 진행하는게 옳바른 것 같다.

애플리케이션 계층은 유스케이스에 따라 커맨드 객체를 도메인 모델을 변경하기 위한 무언가로 매핑할 책임을 가진다.

한 계층이 다른 여러개의 커맨드로 매핑하는게 하나의 웹 모델과 도메인 모델간의 매핑보다 코드량이 더 많다.

그렇지만 유지보스 측면에서는 훨씬 쉽다.

 

이 전략은 전역 패턴으로 추천하지는 않고 웹 계층과 애플리케이션 계층간 상태 변경 유스케이스의 경계가 명확할때 빛을 발한다.

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

 

매핑전략은 여러가지 섞어서 쓰고 어떤 매핑 전략이라도 전역 규칙일 필요는 없다.


'단방향' 매핑 전략

이 전략에서는 모든 계층의 모델들이 같은 인터페이스를 구현한다.

 

장점

  • 풍부한 행동이 있는 도메인 모델
  • 애플리케이션 계층 내의 서비스에서 행동에 접근할 수 있다
  • 바깥 계층으로 전달 시 매핑 없어 가능
    • 도메인 객체가 인커밍/아웃고잉 포트가 기대하는 대로 상태 인터페이스를 구현하고 있기 때문

바깥 계층에서는 상태 인터페이스를 이용할지, 계층 전용 모델로 매핑할지 결정할 수 있다.

도메인의 상태를 실수로 변경하는 일이 발생하지 않는다.

 

애플리케이션 계층에서 실제 도메인 모델로 매핑해서 도메인 모델의 행동에 접근할 수 있게 된다.

DDD의 팩터리(factory)라는 개념과 잘 어울린다,

팩터리(factory) : 어떤 특정한 상태로부터 도메인 객체를 재구성할 책임을 가지고 있다.

이 전략은 계층 간의 모델이 비슷할 때 가장 효과적이다. 예시로 읽기 전용 연산의 경우 상태 인터페이스가 필요한 모든 정보를 제공, 웹 계층 모델로 매핑할 필요가 없다.


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

상황에 맞게 최선의 전략을 찾아서 적용하자.

특정 작업에 대해서 최선의 선택이 아님에도 깔끔하게 느껴진다는 이유로 선택하는 것은 무책임하다.

소프트웨어는 시간이 지나면서 변화하기 때문에 언제든 매핑전략이 변경될 수 있다.

팀 내에서 상황에 따른 매핑전략 가이드라인이 필요하다.

가이드라인을 성공적으로 적용하려면 팀원들이 가이드라인을 숙지하고 있어야 한다.

팀 차원에서 지속적으로 논의하고 수정해야한다.


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

매핑 전략에 대한 가이드라인을 정의하고 팀원들이 이 가이드라인을 항상 숙지할 수 있도록 한다.

팀 차원에서 지속적으로 논의하고 수정하면 상황에 따른 매핑 전략을 선택할 수 있다.

그리고 앞 챕터들에서 다룬 내용 중 포트 인터페이스 분리의 내용 중 '좁은 포트'를 사용해야한다.

그렇게 된다면 유스케이스별로 상황에 따른 매핑 전략을 선택할 수 있다.

결국 유지보수성이 뛰어난 코드를 구성할 수 있다는게 저자의 의견이다.

Comments