DDD - 진화하는 설계 의사 결정


이 포스트에서는 시간의 관점에서 소프트웨어 설계를 살펴보고, 소프트웨어 설계가 그 시간동안 어떻게 변화되고 발전하는지에 대해 알아본다.
또한, 소프트웨어 설계 의사 결정의 유지와 발전에 도메인 주도 설계의 도구를 적용하는 법에 대해 알아본다.

훌륭한 솔루션을 설계하는 것도 중요하지만 시간이 지나면서 프로젝트가 발전하더라도 형태를 유지하는 것이 중요하므로 다양한 관점에서 도메인 주도 설계를 바라본다.
즉, 소프트웨어 프로젝트 환경의 변화가 소프트웨어 의사 결정에 어떻게 영향을 미칠 수 있는지와 그에 따라 설계를 발전시키는 법에 대해 알아본다.

여기서는 비즈니스 도메인, 조직 구도, 도메인 지식, 성장이라는 4가지 변화에 대해 알아본다.


목차


1. 도메인 변경

2.1. 하위 도메인 유형 에서 아래 3가지 비즈니스 하위 도메인에 대해 알아보았었다.

  • 핵심 하위 도메인
    • 기업이 경쟁 우위를 확보하기 위해 경쟁자와 다르게 수행하는 활동
  • 일반 하위 도메인
    • 경쟁자와 다르게 수행하고 있지만 경쟁 우위를 제공하지 않는 활동
  • 지원 하위 도메인
    • 모든 회사가 같은 방식으로 하는 활동

그리고 DDD - 휴리스틱 설계 에서는 실행 중인 하위 도메인의 유형이 전략적, 전술적 설계 의사 결정에 어떻게 영향을 미치는지에 대해 알아보았다.

  • 바운디드 컨텍스트의 경계를 설계하는 방법
  • 복잡한 비즈니스 로직을 다루기 위해 사용할 디자인 패턴

비즈니스 도메인의 요구 사항에 따라 소프트웨어를 설계하려면 비즈니스 하위 도메인과 해당 유형을 식별하는 것도 중요하지만, 하위 도메인의 진화에 주의를 기울이는 것도 중요하다.
이러한 변화의 예시에 대해 알아보자.


1.1. 핵심에서 일반으로

A 회사가 경쟁 우위를 확보하고 있던 기능 a 에 대해 경쟁사가 좀 더 혁신적인 기술을 내놓아서 상용 제품으로 제공되면 A 회사 입장에서 기능 a 는 핵심 하위 도메인에서 일반 하위 도메인으로 변경되게 된다.


1.2. 핵심에서 지원으로

하위 도메인의 복잡성이 정당화되지 않을 경우, 즉 복잡성에 비해 수익성이 없는 경우 핵심 하위 도메인은 지원 하위 도메인으로 전환될 수 있다.
이런 경우 조직은 다른 하위 도메인의 개발을 지원하는데 필요한 최소한의 로직만 남겨두고 불필요한 복잡성을 줄이는 것으로 결정을 내릴 수 있다.


1.3. 일반에서 핵심으로

A 회사에서 상용 솔루션으로 이용하던 기능에 부족한 점이 있어서 결함을 극복한 서비스를 자체 구현으로 진행한다면 이는 일반 하위 도메인에서 핵심 하위 도메인으로 전환된다.

일밤 하위 도메인이 핵심 하위 도메인으로 전환된 좋은 예는 아마존이다.
모든 서비스 제공업체와 마찬가지로 아마존도 서비스를 실행할 인프라가 필요했고, 아마존은 물리적 인프라를 관리하는 방식을 재창조하여 이를 나중에 수익성 있는 비즈니스인 아마존 웹 서비스(AWS) 로 전환하였다.


1.4. 일반에서 지원으로

오픈 소스 솔루션을 사용하는 과정에서 복잡성 대비 얻는 이점을 정당화할 수 없어서 사내 개발 시스템에 의존하게 된다면 일반 하위 도메인에서 지원 하위 도메인으로 전환된다.


1.5. 지원에서 핵심으로

지원 하위 도메인에서 핵심 하위 도메인으로 전환될 전조 증상은 비즈니스 로직이 점점 더 복잡해지는 것이다.
만일 이 복잡성이 회사의 이익에 영향을 미치지 않는다면 우발적 비즈니스 복잡성이고, 회사의 이익에 영향을 미친다면 지원 하위 도메인이 핵심 하위 도메인이 될 징조이다.


1.6. 지원에서 일반으로

사내에서 개발한 시스템 중 특별하거나 복잡한 내용이 없으며, 데이터를 입력하기 위한 CRUD 사용자 인터페이스만 필요한 경우 이는 전형적인 지원 하위 도메인이다.
이 때 사용히고 있는 사내 솔루션에 여러 고급 기능을 더한 오픈 소스 프로젝트가 나와서 사내 솔루션을 버리고 오픈 소스 솔루션과 연동하게 된다면 지원 하위 도메인에서 일반 하위 도메인으로 전환된다.

하위 도메인 유형 변경 요인


2. 전략적 설계 문제

DDD - 바운디드 컨텍스트 연동 에서 본 것처럼 다양한 바운디드 컨텍스트 연동 패턴은 다양한 하위 도메인 유형을 수용한다.
예) 핵심 하위 도메인은 충돌 방지 계층을 사용하여 모델 보호, 오픈 호스트 서비스를 사용하여 구현 모델의 빈번한 변경으로부터 사용자를 보호

하위 도메인 유형의 변경의 영향을 받는 또 다른 통합 패턴은 분리형 노선이다.
지원 하위 도메인과 일반 하위 도메인은 분리형 노선 패턴을 사용할 수 있지만, 핵심 하위 도메인은 한 팀에서만 구현되기 때문에 분리형 노선 패턴을 사용할 수 없고 사용자-제공자 관계가 가장 적합하다.

구현 전략 관점에서 해심 하위 도메인과 지원 하위 도메인의 구현 방법은 아래와 같다.

  • 핵심 하위 도메인
    • 도메인 지식을 보유하기 위해 사내에서 구현
  • 지원 하위 도메인
    • 외부 위탁, 신규 입사자를 위한 교육 도구

3. 전술적 설계 문제

기존의 기술적 설계가 현재 비즈니스 요구를 지원할 수 없는 경우 하위 도메인의 유형 변경이 진행된다.

지원 하위 도메인은 비교적 단순한 디자인 패턴인 트랜잭션 스크립트 혹은 액티브 레코드 패턴으로 구현하는데 만일 지원 하위 도메인이 핵심 하위 도메인이 된다면 기존에 구현된 패턴은 핵심 하위 도메인의 특징인 복잡한 규칙과 복잡성을 가진 비즈니스 로직에는 적합하지 않다.

시간이 지남에 따라 복잡한 규칙과 불변성이 추가되면서 코드베이스도 점점 복잡해지는데 기존 설계가 이러한 복잡한 로직을 지원하지 않아서 새로운 기능을 추가하는 것이 어렵게 된다면 이는 비즈니스 도메인과 설계 의사결정을 재평가하기 위한 신호가 된다.

가능한 설계 옵션이 무엇인지 알고, 이들 사이의 차이점을 알고 있다면 현재 디자인 패턴에서 다른 디자인 패턴으로 마이그레이션하는 것은 어려운 작업이 아니다.
이제 그 예시를 보자.


3.1. 트랜잭션 스크립트에서 액티브 레코드로

트랜잭션 스크립트 패턴과 액티브 레코드 패턴은 모두 절차지향 스크립트를 사용하지만, 자료 구조를 모델링하는 방식에 차이가 있다.
액티브 레코드 패턴은 저장 장치에 매핑하면서 발생하는 복잡성을 자료 구조를 이용하여 캡슐화한다.

트랜잭션 스크립트에서 데이터 작업이 어려워지면 그것을 액티브 레코드 패턴으로 리팩토링하자.

  • 복잡한 자료 구조를 찾아서 액티브 레코드 객체에 캡슐화함
  • DB 에 직접 접근하는 대신 액티브 레코드를 사용하여 모델과 구조를 추상화함

3.2. 액티브 레코드에서 도메인 모델로

액티브 레코드를 조작하는 비즈니스 로직이 점점 더 복잡해지고 중복 사례가 많아진다면 도메인 모델 패턴으로 리팩토링하자.

  • 밸류 오브젝트 식별
    • 불변 객체로 모델링할 수 있는 자료 구조를 찾아서 밸류 오브젝트의 일부로 만듦
  • 자료 구조 분석, 트랜잭션 경계 찾기
    • 액티브 레코드 내부에서만 수정할 수 있게 모든 setter 를 private 수정
    • 그러면 분명히 컴파일이 실패하며, 컴파일 오류는 setter 를 호출하는 곳을 가리킴
    • 그 가리키는 곳을 액티브 레코드의 경계로 리팩토링하면 됨 (= 가리키는 로직을 액티브 레코드 경계 내부로 이동)

3.3. 도메인 모델에서 이벤트 소싱 도메인 모델로

애그리거트 경계가 적절하게 설계된 도메인 모델이 있으면 이벤트 소싱 모델로 전환할 수 있다.

도메인 모델을 이벤트 소싱 도메인 모델로 리팩토링할 때 가장 어려운 점은 기존의 애그리거트의 이력이다.
즉, ‘이력이 없는’ 상태를 이벤트 기반 모델로 마이그레이션해야 하는데 기존에는 과거 상태 변경을 나타내는 데이터가 없기 때문에 과거 이벤트를 생성하거나, 마이그레이션 이벤트를 모델링해야 한다.


3.4. 전환에 필요한 과거 이력 생성

현재 애그리거트의 상태에 도달할 수 있는 과거 이벤트 스트림을 만든 후 이벤트 스트림을 프로젝션해서 원래 구현고 동일한 상태를 나타내게 하는 방법이다.
이 방법은 상태 전환의 전체 히스토리를 복구하는 것이 불가능할 뿐더러 중간에 놓친 이벤트가 몇 개인지도 알 수 없다.


3.5. 마이그레이션 이벤트 모델링

3.4. 전환에 필요한 과거 이력 생성 에 대한 대안으로 지난 이벤트에 대한 데이터가 부족함을 인정하고 명시적으로 이벤트를 모델링하는 방법이 있다.
즉, 현재 상태로 이어질 수 있는 모든 이벤트를 복구하는 대신 마이그레이션 이벤트를 정의하여 기존 애그리거트 인스턴스의 이벤트 스트림을 초기화한다.

이 방법은 과거 데이터의 부족함을 명확히 한여 잘못된 추측을 할 수 없다는 장점이 있다.
단점은 레거시 시스템의 흔적이 이벤트 스토어에 영원히 남아서 항상 마이그레이션 이벤트를 고려해서 프로젝션해야 한다는 점이 있다.


4. 조직 변화

조직의 변화도 시스템 설계에 영향을 줄 수 있다.
DDD - 바운디드 컨텍스트 연동 에서 파트너십 패턴, 공유 커널 패턴, 순응주의자 패턴, 충돌 방지 계층(ACL) 패턴, 오픈 호스트 서비스(OHS) 패턴, 분리형 노선과 같은 다양한 패턴에 대해 알아보았다.

바운디드 컨텍스트는 한 팀에서만 구현할 수 있으므로 조직이 변경되면 바운디드 컨텍스트 연동 또한 그에 맞게 진화해야 한다.

  • 파트너십에서 사용자-제공자로
    • 파트너십 패턴을 팀 간의 강력한 의사소통과 협업을 전제로 함
    • 바운디드 컨테스트 중 하나에 대한 작업이 멀리 위치한 개발 센터로 이동하게 되면 이는 의사소통에 부정적인 영향을 주게 되므로 파트너십 패턴에서 사용자-제공자 패턴으로 전환하는 것이 적절할 수 있음
  • 사용자-제공자에서 분리형 노선으로
    • 지리적 거리나 조직 내부 정치로 인해 심각한 의사소통 문제가 발생할 수 있음
    • 이럴 경우 기능을 복제하는 것이 더 효과적일 수 있음

5. 도메인 지식

핵심 하위 도메인은 로직이 복잡하고 자주 변경될 것으로 예상되며, 모델링은 지속적인 과정이다. 비즈니스 도메인에 대한 더 많은 지식을 습득함에 따라 모델도 개선되어야 한다.

비즈니스 도메인의 복잡성은 처음엔 잘 드러나지 않다가 기능이 추가됨에 따라 점점 더 많은 엣지 케이스, 불변성, 규칙이 발견되기 쉽다.

도메인 로직이 불명확하고 자주 변경되는 경우 바운디드 컨텍스트를 더 넓은 경계로 설계하는 것이 합리적이다.
이후 시간이 지남에 따라 도메인 지식이 발견되고 비즈니스 로직의 변경사항이 안정화되면 이 때 더 좁은 바운디드 컨텍스트 혹은 MSA 로 분해될 수 있다.

바운디드 컨텍스트와 MSA 간의 상호 작용에 대해서는 추후 다룰 예정입니다. (p. 192)

도메인 지식의 변화가 항상 긍정적인 것은 아니다.
시간이 지남에 따라 문서는 부실해지고, 원래 설계했던 사람들은 떠나고, 새로운 기능이 임시변통으로 추가되기 시작하면 결국 코드베이스는 의심스러운 상태의 레거시 시스템이 되어버리고 만다.
이런 도메인 지식의 퇴보를 사전에 방지하는 것이 중요하다.

도메인 지식을 복구하는 효과적인 도구인 이벤트 스토밍 워크숍에 대한 내용은 추후 다룰 예정입니다. (p. 192)


6. 성장

새로운 기능이 지속적으로 추가된다는 것은 시스템이 성공적이라는 신호이지만 반대로 성장을 하면서 코드베이스가 ‘커다란 진흙 덩어리’로 성장할 수도 있다.
커다란 진흙 덩어리를 초래하는 규제없는 성장은 설계의 의사 결정을 재평가하지 않고 소프트웨어 시스템의 기능을 확장한 결과이다.

성장에 따른 복잡성을 다루는 기본 원칙은 우발적 복잡성, 즉 오래된 설계의 결정으로 발생하는 복잡성을 식별하고 제거하는 것이다.
비즈니스 도메인의 본질적인 복잡성은 도메인 주도 설계 도구와 관행을 사용하여 관리해야 한다.

DDD 는 먼저 비즈니스 도메인과 전략적 구성 요소를 분석하고, 비즈니스 도메인과 관련된 모델을 설계한 후, 코드에서 해당 모델을 설계 및 구현하는 프로세스를 따랐다.
성장 중심의 복잡성을 다루기 위해 동일한 과정을 적용해보자.


6.1. 하위 도메인

하위 도메인의 경계는 식별하기 어려울 수 있기 때문에 결과적으로 완벽한 경계를 찾기 위한 노력 대신 유용한 경계를 찾기 위해 노력해야 한다.

비즈니스 도메인이 성장함에 따라 하위 도메인의 경계가 흐려질 수 있기 때문에 이미 식별된 하위 도메인을 다시 확인하여 응집된 유스케이스(= 동일한 데이터 집합에서 동작하는 유스케이스 집합) 에서 휴리스틱을 활용하여 하위 도메인을 나누는 지점을 다시 식별하는 것이 중요하다.

하위 도메인과 해당 유형에 대한 정보가 정확할수록 각 하위 도메인에 대한 기술 솔루션을 효과적으로 선택할 수 있다.
비즈니스 전략 관점에서 가장 중요한 부분에 노력을 투자할 수 있도록 항상 모든 하위 도메인에서 핵심 하위 도메인을 최대한 추출하는 것을 목표로 삼아야 한다.


6.2. 바운디드 컨텍스트

프로젝트가 발전하고 성장함에 따라 바운디드 컨텍스트가 초점을 잃고 다양한 문제와 관련된 로직이 늘어나는 것은 흔한 일인데 이는 우발적 복잡성이다.
이 때는 하위 도메인과 마찬가지로 바운디드 컨텍스트의 경계를 다시 살펴보는 것이 중요하다.

많은 바운디드 컨텍스트가 시간이 지남에 따라 다른 바운디드 컨텍스트를 호출하지 않고는 작업을 완료할 수 없게 된다면 이는 비효율적인 모델의 신호이며, 바운디드 컨텍스트의 경계를 재설계하여 바운디드 컨텍스트 각각의 자율성을 높여야 한다.


6.3. 애그리거트

애그리거트는 가능한 작게 유지하고, 비즈니스 도메인에서 강력하게 일관적인 상태를 유지해야 하는 객체만 포함해야 한다.
하지만 운영을 하다보면 이미 있는 애그리거트에 새로운 기능을 추가하는 것이 편할 수 있다.
비즈니스에 따라 강력한 일관성을 유지할 필요가 없는 데이터까지 포함되면서 애그리거트가 커진다면 이는 제거해야 하는 우발적 복잡성으로 보아야 한다.


정리하며..

  • 비즈니스 도메인이 발전하에 따라 하위 도메인에 대한 변경 사항을 식별하고 시스템 설계에서 조치를 취해야 함
  • 조직 구조의 변화는 팀 간의 의사 소통과 협력, 바운디드 컨텍스트를 통합하는 방식에 영향을 줄 수 있음
  • 하위 도메인의 기능이 확장되면 더 나은 설계 의사 결정을 내릴 수 있도록 더 세분화된 하위 도메인 경계를 식별하려고 노력해야 함
  • ‘여러 방면에 다재다능한’ 바운디드 컨텍스트가 되는 것을 허용하면 안됨
  • 바운디드 컨텍스트에 포함된 모델이 특정 문제를 해결하는데 중점을 두고 있는지 확인해보아야

참고 사이트 & 함께 보면 좋은 사이트

본 포스트는 블라드 코노노프 저자의 도메인 주도 설계 첫걸음을 기반으로 스터디하며 정리한 내용들입니다.






© 2020.08. by assu10

Powered by assu10