성장 주도 복잡성의 징후가 있는지 서로 다른 경계를 지속해서 확인해야 한다.
경쟁에 뒤처지지 않기 위해서 시간이 지남에 따라 스스로를 재창조하고 변경사항을 제대로 관리해야 한다.
비즈니스 도메인, 조직 구조, 도메인 지식, 성장이라는 네 가지 변화의 방향을 바탕으로 소프트웨어 설계를 발전시키는 방법에 대해 설명할 것이다.
핵심 하위 도메인
기업이 경쟁 우위를 확보하기 위해 경쟁자와 다르게 수행하는 활동
지원 하위 도메인
회사가 경쟁자와 다르게 하고 있지만 경쟁 우위를 제공하지 않는 활동
일반 하위 도메인
모든 회사가 같은 방식으로 하는 일
비즈니스 하위 도메인 유형은 다음과 같은 설계 의사결정에 영향을 미친다.
- 바운디드 컨텍스트의 경계를 설계하는 방법
- 컨텍스트 간의 연동을 조율하는 방법
- 복잡한 비즈니스 로직을 다루기 위해 사용할 디자인 패턴
고유한 솔루션이 상용 제품으로 제공되면 핵심 하위 도메인은 일반 하위 도메인으로 바뀐다.
상용 솔루션을 사용하다가 회사에 최적화된 솔루션을 사용하기 위해 자체 구현으로 교체했다.
아마존은 자신들의 서비스를 실행할 인프라를 관리하는 방식을 재창조
하였고, 이것이 핵심 하위 도메인인 아마존 웹 서비스로 전환되었다.
회사에 최적화된 솔루션을 통해 보다 정확하고 전략적으로 관리할 수 있다.
사내에서 자체 구현했던 간단한 CRUD와 같은 솔루션은 비즈니스에 미치는 영향이 적어 우선순위가 낮다.
기존 솔루션보다 더 많은 기능을 제공하는 오픈소스 솔루션과 연동하게 되면 일반 하위 도메인으로 바뀐다.
회사에서 비용을 줄이거나 추가 수익을 창출하는 방식으로 지원 로직을 최적화하는 방법을 찾는 경우 핵심 하위 도메인으로 바뀐다.
지원 하위 도메인의 비즈니스 로직이 점점 복잡해지는 것은 핵심 하위 도메인의 전조 증상이다.
회사의 이익에 영향을 미치지 않는다면 우발적 비즈니스 복잡성이지만, 수익성이 개선된다면 핵심 하위 도메인이 될 징조이다.
핵심 하위 도메인의 복잡성에 비해 수익성이 없는 경우 지원 하위 도메인으로 전환될 수 있다.
다른 하위 도메인의 개발을 지원하는 데 필요한 최소한의 로직만 남겨 복잡성을 줄일 수 있다.
오픈소스 솔루션과 통합 과정에서 복잡성 대비 얻는 이점을 정당화할 수 없다면 지원 하위 도메인으로 전환될 수 있다.
하위 도메인 유형의 변경은 전략적 설계 의사결정에 영향을 준다.
핵심 하위 도메인은 충돌 방지 계층을 사용해 모델을 보호하고, 오픈 호스트 서비스를 사용해 구현 모델의 빈번한 변경으로부터 사용자를 보호해야 한다.
지원 하위 도메인과 일반 하위 도메인에 사용되는 분리형 노선 패턴은 변경의 영향을 받는다.
하위 도메인이 핵심 하위 도메인으로 변경되면 중복을 허용하지 않아 사용자-제공자 관계가 적합하다.
지원 하위 도메인은 외부에 외탁하거나 신규 입사자를 위한 훈련용 도구
로 사용할 수 있지만, 핵심 하위 도메인은 반드시 사내에서 구현해야 한다.
하위 도메인의 유형 변경을 나타내는 주요 지표는 기존의 기술적 설계가 현재 비즈니스 요구를 지원할 수 없는 경우다.
기존 설계가 복잡한 로직을 지원하지 않아 기능을 추가하는 것이 고통
스럽다면 비즈니스 도메인과 설계 의사결정을 재평가하기 위한 신호가 된다.
트랜잭션 스크립트에서 데이터 작업이 어려워지면 액티브 레코드 패턴으로 리팩터링해야 한다.
데이터베이스에 직접 접근하는 대신 액티브 레코드를 사용하여 모델과 구조를 추상화해야 한다.
액티브 레코드를 조작하는 비즈니스 로직이 복잡해지고 불일치 및 중복 사례가 많아진다면 도메인 모델 패턴으로 리팩터링해야 한다.
밸류 오브젝트를 식별하는 것을 시작으로 자료구조를 분석하고 트랜잭션 경계를 찾아야 한다.
모든 상태 수정 로직을 명시적인 액티브 레코드의 경계로 리팩터링한다.
이때 비즈니스 규칙과 불변성을 지속적으로 확인하기 위해 어떤 계층이 필요한지 확인해야 한다.
이는 애그리거트의 좋은 후보가 되고, 강한 일관성을 유지하는 데 필요한 최소 데이터를 찾아 해당 경계에 따라 계층을 분해해야 한다.
마지막으로 애그리거트의 루트 또는 퍼블릭 인터페이스의 엔드포인트를 식별해야 한다.
애그리거트 경계가 잘 설계된 도메인 모델은 이벤트 소싱 모델로 전환할 수 있다.
애그리거트의 데이터를 직접 수정하는 대신 생명주기를 나타내는 데 필요한 도메인 이벤트를 모델링한다.
이력이 없는
상태를 이벤트 기반 모델로 마이그레이션하는 것은 가장 어렵다.
과거 상태 변경을 나타내는 세분화된 데이터가 없어 과거 이벤트를 생성하거나 마이그레이션 이벤트를 모델링해야 한다.
각 애그리거트를 위한 대략적인 이벤트 스트림을 생성하고, 변환된 모델이 생성된 이벤트 스트림을 프로젝션해서 원래 구현과 동일한 상태를 나타내게 한다.
비즈니스 로직 관점에서 애그리거트의 인스턴스가 초기화되었다고 가정하고 도메인 이벤트를 통해 모든 가정을 나타낸다.
[
{
"lead-id": 12,
"event-id": 0,
"event-type": "lead-initialized",
"first-name": "Shauna",
"last-name": "Mercia",
"phone-number": "555-4753"
},
{
"lead-id": 12,
"event-id": 1,
"event-type": "contacted",
"timestamp": "2020-05-27T12:02:12.51Z"
},
{
"lead-id": 12,
"event-id": 2,
"event-type": "order-submitted",
"payment-deadline": "2020-05-30T12:02:12.51Z",
"timestamp": "2020-05-27T12:02:12.51Z"
},
{
"lead-id": 12,
"event-id": 3,
"event-type": "payment-confirmed",
"status": "converted",
"timestamp": "2020-05-27T12:38:44.12Z"
}
]
하지만 이 접근 방식은 전체 히스토리를 복구하는 것이 불가능하고 놓친 이벤트가 몇 개인지 알 수 없다는 단점을 염두에 두어야 한다.
과거 이벤트에 대한 지식 부족을 인정하고, 명시적으로 이벤트를 모델링하는 방법도 있다.
현재 상태로 이어지는 모든 이벤트를 복구하는 대신 마이그레이션 이벤트를 정의하고 기존 애그리거트 인스턴스의 이벤트 스트림을 초기화한다.
{
"lead-id": 12,
"event-id": 0,
"event-type": "migrated-from-legacy",
"first-name": "Shauna",
"last-name": "Mercia",
"phone-number": "555-4753",
"status": "converted",
"last-contacted-on": "2020-05-27T12:02:12.51Z",
"order-placed-on": "2020-05-27T12:02:12.51Z",
"converted-on": "2020-05-27T12:38:44.12Z",
"followup-on": null
}
즉, 누구도 이벤트 스트림이 애그리거트 인스턴스의 생명주기 동안 발생한 모든 도메인 이벤트를 포착한다는 잘못된 추측을 할 수 없다.
하지만 레거시 시스템의 흔적이 이벤트 스토어에 영원히 남는다는 단점이 있다.
조직 자체의 변화가 시스템 설계에 영향을 줄 수 있다.
조직 구조의 변화는 팀 의사소통 및 협업 수준에 영향을 미치고 결과적으로 바운디드 컨텍스트를 통합하는 방식에 영향을 준다.
조직 구조가 변함에 따라 바운디드 컨텍스트의 연동 패턴은 그에 맞게 진화해야 한다.
파트너십 패턴은 팀 간의 강력한 의사소통과 협업을 전제로 하기 때문에 위치에 따른 영향을 많이 받는다.
위치가 멀이지면 팀 의사소통에 부정적인 영향을 주며 사용자-제공자 관계로 이동하는 것이 적절하다.
팀에 의사소통 문제가 생기는 일이 일어나면 시간이 지남에 따라 더 많은 통합 문제를 겪게 된다.
이런 경우 서로의 꼬리를 쫓는 대신 기능을 복제하는 것이 더 효과적일 수 있다.
성공적인 소프트웨어 시스템을 설계하는 데 도메인 지식이 반드시 필요하다.
전략적 설계 관점에서 볼 때 도메인 지식 수준에 따라 바운디드 컨텍스트의 경계를 설계하는 것은 유용한 휴리스틱이다.
도메인 로직이 불명확하고 자주 변경되는 경우 바운디드 컨텍스트를 넓은 경계로 설계하는 것이 합리적이다.
도메인 지식이 발견되고 비즈니스 로직의 변경사항이 안정됨에 따라 넓은 바운디드 컨텍스트를 좁은 바운디드 컨텍스트 또는 마이크로서비스로 분해할 수 있다.
새로운 도메인 지식이 발견되면 설계를 발전시키고 회복성을 높여야 한다.
도메인 지식의 퇴보는 사전에 방지하는 것이 중요하다.
성장은 시스템이 건강하다는 신호다.
하지만 반대로 커다란 진흙 덩어리
로 성장할 수도 있다.
커다란 진흙 덩어리는 엉터리 구조의 거대하고 허술한 와이어 무더기와 강력 접착 테이프 범벅의 스파게티 코드 정글과도 같다.
이러한 시스템의 규제되지 않은 성장과 반복적이고 편의에 따른 수리의 명백한 징후를 보여준다.
이러한 잘못된 성장은 설계의 의사결정을 재평가하지 않고 기능을 확장한 결과다.
많은 도메인 주도 설계 도구가 경계 설정에 관한 것이기 때문에 성장이 설계 의사결정에 미치는 영향을 파악하는 것은 중요하다.
성장에 따른 복잡성을 다루는 기본 원칙은 오래된 설계의 결정으로 발생하는 복잡성을 식별하고 제거하는 것이다.
하위 도메인을 통해 다양한 비즈니스 가치의 구성요소를 식별하고 적절한 도구를 사용해 솔루션을 설계하고 구현할 수 있어야 한다.
이미 식별된 하위 도메인을 다시 확인하고 응집된 유스케이스에서 휴리스틱을 활용해 하위 도메인을 나누는 지점을 다시 식별하는 것이 중요하다.
하위 도메인과 해당 유형에 대한 정보가 정확할수록 각 하위 도메인에 대한 기술 솔루션을 효과적으로 선택할 수 있다.
추출하고 명시적으로 만들 수 있는 내부 하위 도메인을 식별하는 것은 핵심 하위 도메인에 특히 중요하다.
바운디드 컨텍스트의 경계를 다시 살펴보는 것은 중요하고, 우발적 복잡성을 피해 특정 문제를 해결하는 데 초점을 맞춘 바운디드 컨텍스트를 추출하여 모델을 단순화할 수 있도록 해야 한다.
성장은 기존의 암시적 설계 문제를 명확하게 만들 수 있어 바운디드 컨텍스트의 경계를 재설계하여 각각의 자율성을 높여야 한다.
애그리거트의 경계를 설계하기 위해 아래 원칙을 사용한다.
애그리거트는 가능한 한 작게 유지하고, 비즈니스 도메인에서 강력하게 일관적인 상태를 유지해야 하는 객체만 포함한다.
해당 원칙을 지키지 않는다면 개발이 편할 수 있지만, 강력하게 일관성을 유지할 필요가 없는 데이터까지 포함되면서 이를 우발적 복잡성으로 볼 수 있다.
비즈니스 기능을 전담 애그리거트로 추출하면 기존 애그리거트가 단순해지고 잠재적으로 해당 애그리거트가 속한 바운디드 컨텍스트도 단순해진다.
기업에서 영원한 것은 변화한다는 사실뿐이다.
끊임없이 진화하고 스스로를 재창초하는 변화를 설계 프로세스의 최우선으로 다뤄야 한다.
하위 도메인
비즈니스 도메인이 발전함에 따라 하위 도메인에 대한 변경사항을 식별하고 시스템 설계에서 조치를 취해야 한다.
과거의 의사결정이 현재 상태에 부합하는지 확인하고, 현재 비즈니스 전략과 요구사항에 적합한 설계를 발전시켜야 한다.
조직 구조
조직 구조의 변화가 팀 간의 의사소통과 협력, 바운디드 컨텍스트의 연동 패턴에 영향을 준다는 점을 인식해야 한다.
시간이 지나면서 더 많은 도메인 지식이 발견되면 이를 전략적, 전술적 설계 의사결정을 발전시키는 데 활용해야 한다.
성장
소프트웨어 성장은 원하는 유형의 변화지만, 제대로 관리되지 않으면 시스템 설계에 치명적인 영향을 준다.
- 하위 도메인이 확장되면 세분화된 하위 도메인 경계를 식별해야 한다.
- 바운디드 컨텍스트에 포함된 모델은 특정 문제를 해결하는 데 중점을 두어야 한다.
- 애그리거트의 경계를 최대한 작게 유지하고, 휴리스틱을 사용해 비즈니스 로직을 새로운 애그리거트로 추출할 가능성을 두어야 한다.
성장 주도 복잡성의 징후가 있는지 서로 다른 경계를 지속해서 확인하고, 우발적 복잡성을 제거하고 도메인 주도 설계 도구를 사용해 비즈니스 도메인의 본질적 복잡성을 관리해야 한다.
시스템이 성장하게 되면 그에 따라 설계가 발전되어야 한다.
이를 위해 경계를 끊임없이 확인해 진화하고 스스로를 재창조해야 한다.
비즈니스 도메인의 복잡성을 다루는 각각의 패턴에 대해 정확하게 알고 있어야 변화에 맞춰 모델을 변경하고, 설계를 발전시킬 수 있다.