이 포스트는 📔 도메인 주도 개발 시작하기 책을 읽고 공부한 내용을 정리한 포스트입니다.
이전 장에서 살펴본 것과 같이 아키텍처를 설계할 때 출현하는 전형적인 네 가지 영역은 아래와 같다.
네 영역을 구상할 때 많이 사용하는 아키텍처는 계층 구조를 띈다.
표현 → 응용 → 도메인 → 인프라스트럭처
계층 구조는 특성상 상위 계층에서 하위 계층으로의 의존만 존재하고 하위 계층은 상위 계층에 의존하지 않는다. 엄격한 계층 구조를 따르면 상위 계층은 바로 아래 계층에 의존만을 가져야 하지만, 구현의 편리함을 위해서 계층 구조를 유연하게 적용하는 경우도 존재한다.
예를 들어 응용 계층은 도메인 계층에 의존하지만 외부 시스템을 활용하기 위해서 인프라스트럭처 계층에 의존하기도 한다.
하지만 이러한 의존은 문제점을 발생시키기도 한다. 응용 계층이 인프라스트럭처 영역의 기술에 완전하게 의존하게 되는 경우(인프라스트럭처에 특화된 코드 활용 등) 응용 계층만 단독으로 테스트하기 어렵다는 점과 추후 구현 방식을 변경하기 어렵다라는 점이다.
테스트의 어려움과 기능 확장의 어려움이 발생하는 이유는 고수준의 모듈이 저수준의 모듈에 의존하고 있었기 때문이다. 이 문제를 해결하기 위해 Dependency Inversion Principle(의존 역전 원칙)을 활용한다.
의존 역전 원칙(이하 DIP)는 인터페이스 추상화 를 활용해서 저수준의 모듈이 고수준의 모듈에 의존하도록 의존하도록 바꾼다.
인터페이스 추상화를 활용하면 기능 확장을 위한 구현 기술 교체의 경우 저수준 구현 객체를 생성하는 방식을 변경함으로써 해결이 가능하고 테스트의 경우는 Mock을 활용해 대체가 가능하다.
DIP를 적용함에 있어 주의할 점은 하위 기능을 추상화한 인터페이스는 고수준의 모듈의 관점에서 도출해야 한다는 점이다. 저수준의 관점에서 추상화를 도출한다면 결국 고수준의 모듈이 저수준의 추상화된 모듈에 의존하게 됨으로 DIP를 적용한 이유가 없어지게 된다.
도메인 영역을 구성하는 요소는 아래와 같다.
요소 | 설명 |
---|---|
엔티티 Entity | 고유 식별자를 갖는 객체로 자신만의 라이프사이클을 갖는다. 도메인의 고유한 개념을 표현하며 도메인 모델의 데이터를 포함하여 해당 데이터와 관련된 기능을 함께 제공한다. |
밸류 Value | 고유의 식별자를 갖지 않는 객체로서 주로 개념적인 값을 표현할때 사용한다. 엔티티의 속성 뿐만 아니라 다른 밸류 타입의 속성으로 사용할 수 있다. |
애그리거트 Aggregate | 연관된 엔티티와 밸류 객체를 개념적으로 하나로 묶은 것이다. |
리포지터리 Repository | 도메인 모델의 영속성을 처리한다. |
도메인 서비스 Domain Service | 특정 엔티티에 속하지 않는 도메인 로직을 제공한다. 도메인 로직이 여러 엔티티와 밸류를 필요로 하면 도메인 서비스에서 로직을 구현한다. |
이전 장에서 엔티티와 밸류에 대해서 살펴보았기에 도메인 모델의 엔티티와 DB 관계형 모델의 엔티티의 차이점 위주로 살펴보자면...
도메인이 커질수록 개발하는 도메인 모델도 커지면서 많은 엔티티와 밸류가 생성되고 모델은 점점 더 복잡해진다. 이때 상위 수준에서 모델을 관리하지 않으면 큰 틀에서 모델을 관리하기 어려운 상황에 빠지기 쉽다.
도메인 모델을 상위 수준에서 바라볼 수 있어야 전체 모델의 관계와 개별 모델을 이해하기 쉽다. 이를 위해 사용하는 것이 바로 애그리거트이다.
애그리거트는 관련 객체를 하나로 묶은 군집을 이야기한다. 이를 사용하면 도메인을 객체 군집 단위로 모델을 바라볼 수 있고, 관계에 기반해서 도메인을 이해 및 관리할 수 있다.
도메인 객체의 지속적인 사용을 위해 보관할 수 있게 해주는 도메인 모델을 리포지터리라고 한다.
리포지터리는 애그리거트 단위로 도메인 객체를 저장하고 조회하는 기능을 정의한다.
소프트웨어에서 요청이 어떻게 처리되는지 흐름을 간단하게 살펴보자면,
사용자가 애플리케이션에 기능 실행을 요청하면 표현 영역에 요청이 도달한다.
표현 영역에서는 데이터에 대한 형식 검증을 진행하고 서비스가 요구하는 형식으로 변환 후 전달한다.
서비스는 적합한 도메인을 이용해 기능을 구현한 후 결과를 리턴한다.
리턴된 결과는 표현 형식에 맞게 변환되어 사용자에게 전달된다.
위와 같다.
인프라스트럭처에 대한 의존 문제를 이전에 살펴보았는데 무조건 인프라스트럭처에 대한 의존을 없앨 필요는 없다.
구현의 편리함은 DIP의 장점만큼 중요하기에 DIP의 장점을 해치지 않는 범위에서 의존을 갖고 가는 거이 나쁘지 않다고 본다.
도메인의 크기가 크다면 도메인을 하위 도메인으로 나누고 각 하위 도메인마다 별도의 패키지를 구성하는 것이 좋다.
모듈 구조의 세분화는 정해진 규칙이 존재하지 않지만, 한 패키지에 너무 많은 타입이 몰려 코드를 찾기 불편한 경우는 배제하는 것이 좋다.