다음을 통해 공유


DDD 지향 마이크로 서비스 디자인

팁 (조언)

이 콘텐츠는 .NET Docs 또는 오프라인으로 읽을 수 있는 다운로드 가능한 무료 PDF로 제공되는 컨테이너화된 .NET 애플리케이션용 .NET 마이크로 서비스 아키텍처인 eBook에서 발췌한 내용입니다.

컨테이너화된 .NET 애플리케이션을 위한 .NET 마이크로서비스 아키텍처 eBook의 표지 썸네일.

DDD(도메인 기반 디자인)는 사용 사례와 관련된 비즈니스 현실을 기반으로 모델링을 옹호합니다. 애플리케이션을 빌드하는 컨텍스트에서 DDD는 문제에 대해 도메인으로 설명합니다. 독립적인 문제 영역을 제한된 컨텍스트(각 제한된 컨텍스트는 마이크로 서비스와 상관 관계)로 설명하고 이러한 문제에 대해 이야기할 공통 언어를 강조합니다. 또한 풍부한 모델이 있는 도메인 엔티티(빈약한 도메인 모델 사용 안함), 값 객체, 집합 및 집합 루트(또는 루트 엔티티) 규칙과 같은 많은 기술 개념 및 패턴을 내부 구현을 지원하기 위해 제안합니다. 이 섹션에서는 이러한 내부 패턴의 디자인 및 구현을 소개합니다.

경우에 따라 이러한 DDD 기술 규칙 및 패턴은 DDD 접근 방식을 구현하기 위한 가파른 학습 곡선이 있는 장애물로 인식됩니다. 그러나 중요한 부분은 패턴 자체가 아니라 비즈니스 문제에 맞게 코드를 구성하고 동일한 비즈니스 용어(유비쿼터스 언어)를 사용하는 것입니다. 또한 중요한 비즈니스 규칙을 사용하여 복잡한 마이크로 서비스를 구현하는 경우에만 DDD 접근 방식을 적용해야 합니다. CRUD 서비스와 같은 더 간단한 책임은 더 간단한 방법으로 관리할 수 있습니다.

마이크로 서비스를 디자인하고 정의할 때 경계를 그릴 위치는 핵심 작업입니다. DDD 패턴은 도메인의 복잡성을 이해하는 데 도움이 됩니다. 각 경계 컨텍스트에 대한 도메인 모델의 경우 도메인을 모델링하는 엔터티, 값 개체 및 집계를 식별하고 정의합니다. 컨텍스트를 정의하는 경계 내에 포함된 도메인 모델을 빌드하고 구체화합니다. 그리고 마이크로 서비스의 형태로 명시적입니다. BC 또는 비즈니스 마이크로 서비스는 여러 물리적 서비스로 구성될 수 있지만 이러한 경계 내의 구성 요소는 결국 마이크로 서비스가 됩니다. DDD는 경계에 관한 것이므로 마이크로 서비스도 마찬가지입니다.

마이크로 서비스 컨텍스트 경계를 상대적으로 작게 유지

제한된 컨텍스트 간에 경계를 배치할 위치를 결정하면 두 가지 경쟁 목표의 균형을 맞춥니다. 첫째, 처음에는 가능한 가장 작은 마이크로 서비스를 만들려고 하지만 주 드라이버가 되어서는 안 됩니다. 응집력이 필요한 경계를 만들어야 합니다. 둘째, 마이크로 서비스 간의 수다스러운 통신을 방지하려고 합니다. 이러한 목표는 서로 모순 될 수 있습니다. 새로운 제한된 컨텍스트를 분리하려는 각 추가 시도와 함께 통신 경계가 빠르게 증가하는 것을 볼 때까지 시스템을 가능한 한 많은 작은 마이크로 서비스로 분해하여 균형을 유지해야 합니다. 응집력은 단일 경계 컨텍스트 내의 키입니다.

클래스를 구현할 때 부적절한 친밀감 코드 냄새 와 유사합니다. 두 마이크로 서비스가 서로 많은 공동 작업을 수행해야 하는 경우 동일한 마이크로 서비스일 수 있습니다.

이 측면을 보는 또 다른 방법은 자율성입니다. 마이크로 서비스가 요청을 직접 서비스하기 위해 다른 서비스에 의존해야 하는 경우 실제로 자율적이지 않습니다.

DDD 마이크로 서비스의 계층

비즈니스 및 기술 복잡성이 큰 대부분의 엔터프라이즈 애플리케이션은 여러 계층으로 정의됩니다. 계층은 논리적 아티팩트이며 서비스 배포와 관련이 없습니다. 개발자가 코드의 복잡성을 관리하는 데 도움이 됩니다. 다른 계층(예: 도메인 모델 계층과 프레젠테이션 계층 등)에는 형식이 다를 수 있으며, 이러한 형식 간에 변환을 위임합니다.

예를 들어 데이터베이스에서 엔터티를 로드할 수 있습니다. 그런 다음 해당 정보의 일부 또는 다른 엔터티의 추가 데이터를 포함한 정보 집계를 REST Web API를 통해 클라이언트 UI로 보낼 수 있습니다. 여기서 요점은 도메인 엔터티가 도메인 모델 계층 내에 포함되어 있으며 프레젠테이션 계층과 같이 해당 엔터티가 속하지 않는 다른 영역으로 전파되어서는 안 된다는 것입니다.

또한 집계 루트(루트 엔터티)로 제어되는 항상 유효한 엔터티( 도메인 모델 계층 섹션의 디자인 유효성 검사 참조)가 있어야 합니다. 따라서 UI 수준에서 일부 데이터의 유효성을 검사하지 않을 수 있으므로 엔터티는 클라이언트 뷰에 바인딩되지 않아야 합니다. 이러한 이유가 ViewModel의 존재 이유입니다. ViewModel은 프레젠테이션 계층 요구 사항을 위한 전용 데이터 모델입니다. 도메인 엔터티는 ViewModel에 직접 속하지 않습니다. 대신 ViewModels와 도메인 엔터티 간에 변환해야 하며 그 반대의 경우도 마찬가지입니다.

복잡성을 해결할 때는 해당 엔터티 그룹(집계)과 관련된 모든 고정 및 규칙이 집계 루트인 단일 진입점 또는 게이트를 통해 수행되도록 하는 집계 루트로 제어되는 도메인 모델을 갖는 것이 중요합니다.

그림 7-5는 eShopOnContainers 애플리케이션에서 계층화된 디자인이 구현되는 방법을 보여줍니다.

도메인 기반 디자인 마이크로 서비스의 계층을 보여 주는 다이어그램

그림 7-5. eShopOnContainers의 순서 지정 마이크로 서비스의 DDD 계층

Ordering과 같은 DDD 마이크로 서비스의 세 계층입니다. 각 계층은 VS 프로젝트입니다. 애플리케이션 계층은 Ordering.API이고 도메인 계층은 Ordering.Domain이고 인프라 계층은 Ordering.Infrastructure입니다. 각 계층이 특정 다른 레이어와만 통신하도록 시스템을 디자인하려고 합니다. 라이브러리 간에 설정된 종속성을 명확하게 식별할 수 있으므로 계층이 다른 클래스 라이브러리로 구현되는 경우 이 방법을 적용하는 것이 더 쉬울 수 있습니다. 예를 들어 도메인 모델 계층은 다른 계층에 종속되지 않아야 합니다(도메인 모델 클래스는 일반 이전 클래스 개체 또는 POCO 클래스여야 합니다). 그림 7-6에 표시된 것처럼 Ordering.Domain 계층 라이브러리는 .NET 라이브러리 또는 NuGet 패키지에만 종속되지만 데이터 라이브러리 또는 지속성 라이브러리와 같은 다른 사용자 지정 라이브러리에는 종속성이 없습니다.

Ordering.Domain 종속성의 스크린샷.

그림 7-6. 라이브러리로 구현된 레이어를 사용하면 계층 간의 종속성을 더 잘 제어할 수 있습니다.

도메인 모델 계층

Eric Evans의 뛰어난 책 도메인 기반 디자인 은 도메인 모델 계층 및 애플리케이션 계층에 대해 다음과 같은 내용을 말합니다.

도메인 모델 계층: 비즈니스 개념, 비즈니스 상황에 대한 정보 및 비즈니스 규칙을 나타내는 역할을 담당합니다. 비즈니스 상황을 반영하는 상태는 저장의 기술 세부 정보가 인프라에 위임되더라도 여기에서 제어되고 사용됩니다. 이 계층은 비즈니스 소프트웨어의 핵심입니다.

도메인 모델 계층은 비즈니스가 표현되는 위치입니다. .NET에서 마이크로 서비스 도메인 모델 계층을 구현하는 경우 해당 계층은 데이터와 동작(논리가 있는 메서드)을 캡처하는 도메인 엔터티를 사용하여 클래스 라이브러리로 코딩됩니다.

지속성 무시인프라 무지 원칙에 따라 이 계층은 데이터 지속성 세부 정보를 완전히 무시해야 합니다. 이러한 지속성 작업은 인프라 계층에서 수행해야 합니다. 따라서 이 계층은 인프라에 직접 종속되지 않아야 합니다. 즉, 중요한 규칙은 도메인 모델 엔터티 클래스가 POCO여야 한다는 것입니다.

도메인 엔터티에는 Entity Framework 또는 NHibernate와 같은 데이터 액세스 인프라 프레임워크에 대한 직접 종속성(예: 기본 클래스에서 파생)이 없어야 합니다. 이상적으로 도메인 엔터티는 인프라 프레임워크에 정의된 형식에서 파생되거나 구현되어서는 안 됩니다.

Entity Framework Core와 같은 대부분의 최신 ORM 프레임워크는 도메인 모델 클래스가 인프라에 결합되지 않도록 이 방법을 허용합니다. 그러나 Azure Service Fabric의 행위자 및 신뢰할 수 있는 컬렉션과 같은 특정 NoSQL 데이터베이스 및 프레임워크를 사용하는 경우 POCO 엔터티를 항상 사용할 수 있는 것은 아닙니다.

도메인 모델에 대한 지속성 무시 원칙을 따르는 것이 중요하더라도 지속성 문제를 무시해서는 안 됩니다. 물리적 데이터 모델과 해당 모델이 엔터티 개체 모델에 매핑되는 방법을 이해하는 것은 여전히 중요합니다. 그렇지 않으면 불가능한 디자인을 만들 수 있습니다.

또한 이 측면이 관계형 데이터베이스용으로 설계된 모델을 가져와서 NoSQL 또는 문서 지향 데이터베이스로 직접 이동할 수 있는 것은 아닙니다. 일부 엔터티 모델에서는 모델이 적합할 수 있지만 일반적으로는 그렇지 않습니다. 여전히 스토리지 기술과 ORM 기술을 기반으로 엔터티 모델이 준수해야 하는 제약 조건이 있습니다.

애플리케이션 계층

애플리케이션 계층으로 이동하면 Eric Evans의 책 도메인 기반 디자인을 다시 인용할 수 있습니다.

애플리케이션 계층: 소프트웨어에서 수행해야 하는 작업을 정의하고 표현형 도메인 개체가 문제를 해결하도록 지시합니다. 이 계층이 담당하는 작업은 비즈니스에 의미가 있거나 다른 시스템의 애플리케이션 계층과 상호 작용하는 데 필요합니다. 이 계층은 얇게 유지됩니다. 비즈니스 규칙이나 지식을 포함하지 않으며, 대신 작업을 조율하고 다음 계층에 있는 도메인 객체의 협업에 업무를 위임합니다. 비즈니스 상황을 반영하는 상태는 없지만 사용자 또는 프로그램에 대한 작업의 진행률을 반영하는 상태를 가질 수 있습니다.

.NET에서 마이크로 서비스의 애플리케이션 계층은 일반적으로 ASP.NET Core Web API 프로젝트로 코딩됩니다. 이 프로젝트는 마이크로 서비스의 상호 작용, 원격 네트워크 액세스 및 UI 또는 클라이언트 앱에서 사용되는 외부 웹 API를 구현합니다. 여기에는 CQRS 접근 방식, 마이크로 서비스에서 허용하는 명령 및 마이크로 서비스(통합 이벤트) 간의 이벤트 기반 통신을 사용하는 경우의 쿼리가 포함됩니다. 애플리케이션 계층을 나타내는 ASP.NET Core Web API에는 비즈니스 규칙 또는 도메인 지식(특히 트랜잭션 또는 업데이트에 대한 도메인 규칙)이 포함되어서는 안 됩니다. 도메인 모델 클래스 라이브러리에서 소유해야 합니다. 애플리케이션 계층은 작업만 조정해야 하며 도메인 상태(도메인 모델)를 유지하거나 정의해서는 안 됩니다. 비즈니스 규칙의 실행을 도메인 모델 클래스 자체(집계 루트 및 도메인 엔터티)에 위임하여 궁극적으로 해당 도메인 엔터티 내의 데이터를 업데이트합니다.

기본적으로 애플리케이션 논리는 지정된 프런트 엔드에 종속된 모든 사용 사례를 구현하는 위치입니다. 예를 들어 Web API 서비스와 관련된 구현입니다.

목표는 도메인 모델 계층의 도메인 논리, 고정, 데이터 모델 및 관련 비즈니스 규칙의 도메인 논리가 프레젠테이션 및 애플리케이션 계층과 완전히 독립적이어야 한다는 것입니다. 무엇보다도 도메인 모델 계층은 인프라 프레임워크에 직접 의존해서는 안됩니다.

인프라 계층

인프라 계층은 처음에 도메인 엔터티(메모리)에 보관된 데이터가 데이터베이스 또는 다른 영구 저장소에 유지되는 방식입니다. 예를 들어 Entity Framework Core 코드를 사용하여 DBContext를 사용하여 관계형 데이터베이스에 데이터를 유지하는 리포지토리 패턴 클래스를 구현합니다.

앞에서 언급한 지속성 무지인프라 무지 원칙에 따라 인프라 계층은 도메인 모델 계층을 "오염"해서는 안 됩니다. 프레임워크에 대한 하드 종속성을 사용하지 않음으로써 데이터(EF 또는 다른 프레임워크)를 유지하는 데 사용하는 인프라에서 도메인 모델 엔터티 클래스를 제약 없이 유지해야 합니다. 도메인 모델 계층 클래스 라이브러리에는 도메인 코드만 있어야 하며, 소프트웨어의 핵심을 구현하고 인프라 기술에서 완전히 분리된 POCO 엔터티 클래스만 있어야 합니다.

따라서, 그림 7-7에 표시된 것처럼, 당신의 계층 또는 클래스 라이브러리와 프로젝트는 도메인 모델 계층(라이브러리)에 반드시 의존해야 하며, 그 반대는 안 됩니다.

DDD 서비스 계층 간에 존재하는 종속성을 보여 주는 다이어그램

그림 7-7. DDD의 레이어 간 종속성

DDD 서비스의 종속성, 애플리케이션 계층은 도메인 및 인프라에 종속되고 인프라는 도메인에 따라 달라지지만 도메인은 계층에 종속되지 않습니다. 이 계층 디자인은 각 마이크로 서비스에 대해 독립적이어야 합니다. 앞에서 설명한 것처럼 더 간단한 데이터 기반 마이크로 서비스(단일 계층의 단순 CRUD)를 더 간단한 방식으로 구현하면서 DDD 패턴에 따라 가장 복잡한 마이크로 서비스를 구현할 수 있습니다.

추가 리소스