Layered Architecture는 기술적 관심사로부터 도메인 개념을 분리한다. 도메인 주도 설계의 전제 조건은 도메인 영역을 다루는 도메인 계층을 격리시키는 것이다. 하지만 규모가 큰 시스템에서는 격리된 도메인 계층 그 자체도 관리가 되지 않을 정도로 복잡해질 수도 있다.
우리는 개발을 진행할 때 지식 탐구 및 심층 연구를 통해 도메인을 좀 더 깊이 이해하면서, 리팩터링 과정을 거쳐, 도메인 지식의 정수를 추출한 모델을 형상화하여 코드에 반영하게 된다. 이외에 도메인 계층에 있는 복잡한 도메인 모델의 여러 요소들을 분리하여 도메인 본질을 좀 더 값지고 유용한 형태로 뽑아내는 과정인 디스틸레이션(Distillation)이 있다. 이 과정을 거치면 Generic Subdomain(일반 하위 도메인)과 Coherent Mechanism(응집력 있는 매커니즘)가 나타나는데, 이는 우리 도메인 핵심 영역인 Core Domain을 추출하기 위한 과정인 것이다.
규모가 큰 시스템을 설계할 때는 시스템에 존재하는 구성요소가 매우 많은데, 도메인 계층이 너무 복잡해서 도메인의 본질적인 측면이 가려질 때가 있다.
개발을 진행하다보면, 각 개발자들은 각자 자신이 맡은 모듈에 대해서만 전문성을 갖추게 되는데 이는 개발자들끼리 지식 전달이 줄어들게 된다. 시스템은 매끄럽게 통합되기 힘들어지고, 업무 할당의 유연성도 떨어지게 된다. 어떤 행위가 이미 다른 곳에 존재한다는 사실을 개발자가 알지 못하면 코드의 중복도 일어나게 되고 시스템은 더욱 복잡해진다.
도메인 주도 설계 및 개발을 하면서 모델을 정제해나가게 되는데, 설계의 모든 부분이 동일한 시점에 정제되지는 않는다. 따라서 각 모델, 설계에 우선순위를 매겨야 되는데, 도메인 모델을 가치있게 하려면 모델의 핵심적인 측면을 다루기 수월하게 하고 이를 충분히 활용할 수 있어야 한다.
가끔 개발자들은 애플리케이션의 핵심부는 관심없고, 기술적인 내용에 관심이 있어 인프라스트럭처 계층이나 도메인 지식이 없어도 이해할 수 있는 문제만 다루려고 할 때가 있다. 숙련된 개발자가 애플리케이션의 보조적인 기능이나 기술만을 다루는 영역을 맡고, 초보 개발자가 핵심 모델을 다루는 경우도 있다. 소프트웨어 핵심 부분의 설계 및 구현 품질이 낮다면 기술적인 인프라스트럭처나 보조기능이 얼마나 멋지든 사용자에게는 결코 매력적으로 다가오지 않는다.
애플리케이션의 목적에 부합되는 중심적인 모델은 Core Domain(핵심 도메인)으로 구성되는데, 이 영역이야말로 시스템에서 가장 큰 가치가 있는 곳이다.
모델을 요약하고 Core Domain을 찾아 그것을 지원하는 보조적인 역할을 맡은 다수의 모델과 코드로부터 구별해야 한다. 그리고 이 영역에 가장 재능 있는 인력을 할당해야 한다. 시스템의 비전을 수행하기에 충분한 심층 모델을 찾아 유연한 설계로 이어지도록 Core Domain에 노력을 쏟아야 한다. Core Domain에 역량을 쏟는 동시에 설계의 나머지 부분들은 실용적인 수준으로 일반화해서 유지해야 한다.
어느 특정 영역이 경쟁사로부터 비밀스럽게 유지해야 한다면 그것이 바로 Core Domain이다.
Core Domain은 애플리케이션의 성격마다 다를 수 있다. 어느 애플리케이션에는 Core Domain인 것이 다른 애플리케이션에서는 보조 컴포넌트의 역할일 수도 있다.
애플리케이션의 Core Domain을 식별하고 정의하는 것은 도메인에 대한 통찰력과 경험이 깊어질 때 가능한 것이다. 반복적인 개발 주기 및 리팩터링을 통해 Core Domain을 파악하고 이를 구별할 수 있도록 하라.
모델의 일부는 비즈니스적으로 가치가 있는 전문 지식을 담고 있는 핵심 영역이 아님에도 불구하고 도메인 계층에 복잡성을 더하곤 한다. 이런 요소들은 Core Domain을 식별하고 이해하는 일을 어렵게 만들 수 있다.
이런 모델들은 보통 일반적인 기능, 예를 들면 환율 계산이나 위치에 따른 시간대 변환과 같은 것을 다루는 모델인 경우가 많다. 핵심 모델은 아니지만, 그럼에도 애플리케이션이 동작하고 핵심 모델을 지원하는데 있어서 필요하다.
일반적인 모델 요소가 매우 중요하더라도, 전체 도메인 모델에서는 시스템에서 가장 가치가 있는 특별한 측면, 즉 Core Domain을 두드러지게 하고 여기에 가능한 많은 노력과 비용이 실리게끔 구조화해야 한다.
비즈니스적 핵심 영역인 Core Domain이 아니고, 일반적인 기능을 담는 응집력 있는 하위 도메인을 식별하라. 이러한 하위 도메인에서 모델 요소를 추출해 별도 Module에 배치하라.
하위 도메인이 분리되고 나면 남겨진 Core Domain이 두드러지게 되고, 기타 도메인 모델에 의해 복잡해지지 않을 뿐만 아니라 다루기가 한결 수월해져 충분히 활용할 수 있게 된다.
이러한 하위 도메인, Generic Subdomain에는 Core Domain보다 낮은 우선순위를 부여하고 그 일에 핵심 개발자를 배치하지 않도록 한다.
일반적인 기능을 다루는 Generic Subdomain에 대해서는 외부 라이브러리나 다른 하위 시스템으로 대체할 수도 있다.
일반적인 기능, 일반화라는 것이 재사용 가능하다는 말이 아니다. 사내에서 개발하든, 외주 제작하든 스스로 코드를 구현한다고 가정하면 특히 재사용성에 신경 써서는 안 된다. 재사용성에 신경을 써서 Core Domain에 노력을 기울일 수도 있는 시간과 비용을 허비하지 마라. 디스틸레이션의 기본 동기는 Core Domain에 가능한 많은 노력을 쏟게 하고, 보조적인 성격인 Generic Subdomain에는 필요한 만큼 투자하자는 것이다. Generic Subdomain은 Core Domain의 요구사항에 따라가야 한다. 당장 업무에 필요한 부분에 대해서만 모델링하고 구현하라.
구현을 캡슐화하는 것은 객체지향 설계의 표준 원칙이다. 의도를 드러내는 이름이 메서드의 복잡한 알고리즘을 숨긴다면 무엇이 어떻게와 분리된다.
때로는 도메인 모델에 서로 섞여 있는 매커니즘, 계산 로직들로 인해 모델이 복잡해지기도 한다. 문제를 표현해야 되는(What, 비즈니스 영역 모델을 반영하는) 코드가 문제 해결을 위해 알고리즘 및 계산 로직을 제공하는 코드(How)에 의해 불분명해지기도 한다.
이 것은 모델에 문제가 있다는 징후에 해당한다. 더 심층적인 통찰력으로 향하는 리팩터링을 통해 계산 매커니즘을 간결하게 만드는 모델을 찾아야 한다. 매커니즘의 어느 부분이 개념적으로 응집력 있는 부분으로 드러나게 되고, 이를 뽑아내어 별도로 분할한다면 이해하기가 쉬워질 것이다.
개념적으로 응집력 있는 매커니즘(Cohesive Mechanism)을 별도의 경량 프레임워크로 분할하라. 의도를 나타내는 인터페이스(Intention-Revealing Interface)로 프레임워크의 기능을 노출하라. 도메인의 다른 요소들은 문제 해결의 복잡성(어떻게)를 프레임워크에 위임하여 문제(무엇)을 표현하는데 집중할 수 있을 것이다.
이렇게 분리된 응집력 있는 메커니즘은 보조적인 역할에 머물게 되고, 더욱 선언적인 형식의 인터페이스를 통해 매커니즘을 사용하는, 더 작고 더 표현력 있는 Core Domain을 남길 수 있다.
매커니즘은 계산, 알고리즘에만 집중하게 되고, 표현력 있는 도메인 모델과 섞이지 않게 될 뿐만 아니라 Core Domain과 Generic Subdomain 모델들이 사실이나 규칙, 문제를 정형화하게 될 수 있다. 그 결과로, 도메인 모델의 복잡성이 완화되고 훨씬 더 명료해지는 선언적 형식이 가능해진다.
Generic Subdomain과 Cohesive Mechanism은 모두 똑같이 Core Domain의 부담을 더는데 목적이 있다. 둘의 차이점은 각자 맡고 있는 책임의 본질에 있다.
Generic Subdomain은 팀이 도메인을 어떻게 바라보는지에 따라 덜 중심적이고 덜 중요하고 덜 특화된, 하지만 도메인을 나타내는 표현력있는 모델에 토대를 둔다. 반면에 Cohesive Mechanism은 도메인을 나타내는 것이 아니다. 단지 도메인 모델에서 제기되는 일부 성가진 계산 문제를 해결해주는 것이다.
모델이 제안하면 Cohesive Mechanism은 처리한다.
연이은 리팩터링 과정을 통해 Cohesive Mechanism은 좀 더 순수한 매커니즘으로의 디스틸레이션 과정을 거치거나, 미처 인식하지 못했던 일부 모델 개념을 포함한 Generic Subdomain으로 변형될 수 있다.
Mechanism이 담당하는 로직이 대단히 특화되어 애플리케이션 가치의 핵심 부분을 차지하는 경우도 있다. 이 때는 개념적 Core의 일부로 여겨지기도 한다.