이 문서에서는 DDD(도메인 기반 디자인), CQRS 및 이벤트 소싱을 구현하는 프로젝트인 foodtruacker에 대해 자세히 설명합니다. ASP.NET Core를 사용하며 복잡한 비즈니스 도메인의 유지 관리성을 향상시키는 데 중점을 둡니다. 이 프로젝트는 설명 목적으로 단순화된 가상의 비즈니스 사례를 사용합니다. 이 자세한 설명에서는 동기, 기능, 구현 세부 사항 및 관련 기술을 다룹니다.
foodtruacker - DDD, CQRS 및 이벤트 소싱 구현
이 이벤트 중심 프로젝트는 복잡한 비즈니스 영역을 반영하는 시스템을 다룰 때 유지 관리성을 강화한다는 아이디어에 중점을 둔 원칙, 프레임워크 및 아키텍처를 활용합니다. 애플리케이션의 웹 API는 Microsoft의 ASP.NET Core 프레임워크를 기반으로 구축되었으며 도메인 기반 디자인은 물론 CQRS 및 이벤트 소싱 패턴을 구현합니다. 가상의 비즈니스 사례가 이 프로젝트의 기초를 마련하고 이벤트 폭풍 워크숍의 결과입니다.
참고: 이 프로젝트에 도입된 가상의 비즈니스 도메인은 크게 단순화되었으며 관련 사용 사례의 제공자로만 보아야 합니다.
동기 부여
다소 복잡한 비즈니스 도메인이 있는 프로젝트에서 CRUD 작업과 POCO 개체를 사용하는 것이 항상 최선은 아니기 때문에 저는 DDD(도메인 중심 설계)에 대한 연구와 관심을 실제로 구현하기 위해 이 프로젝트를 만들기로 결정했습니다. 소프트웨어 개발 접근 방식.
이 프로젝트에 소개된 가상의 비즈니스 사례는 이벤트 중심이므로 CQRS 및 이벤트 소싱 패턴도 구현하기로 결정했습니다. 두 사람 모두 이번 프로젝트를 연구하면서 눈길을 끌었고, DDD와도 잘 어울린다.
특징
개요
이 프로젝트는 하나의 실행 가능한 웹 API 애플리케이션과 여러 기능 구성 요소로 구성되며 각각은 클래스 라이브러리를 통해 제공됩니다. 코드는 네임스페이스로 구성됩니다. Visual Studio에서 생성된 ASP.NET Core 애플리케이션에서 네임스페이스는 기본적으로 프로젝트의 폴더 구조에서 자동으로 생성됩니다. 이 프로젝트의 폴더(및 네임스페이스) 구조에 대한 개요를 보려면 아래 다이어그램을 참조하세요.
소개
이벤트 스토밍
Alberto Brandolini가 발명한 복잡한 비즈니스 영역의 공동 탐색을 위한 유연한 워크샵 형식입니다. 이는 조직 내의 비즈니스 흐름과 프로세스를 신속하게 개선, 구상, 탐색 및 설계하기 위한 매우 가벼운 방법론입니다.
워크숍은 다양한 전문 지식을 갖춘 사람들이 컬러 스티커 메모를 사용하여 관련 비즈니스 프로세스를 공동으로 레이아웃하는 그룹으로 구성됩니다. EventStorming 워크숍에는 적합한 사람들이 참석하고 스티커 메모를 붙일 수 있는 충분한 표면적이 있어야 합니다. 필수 인력에는 일반적으로 질문할 질문을 아는 사람(일반적으로 개발자)과 답변을 아는 사람(도메인 전문가, 제품 소유자)이 포함됩니다.
이 워크숍의 목적은 참가자들이 서로에게서 배우고, 오해를 드러내고 반박하도록 하며, 예를 들어 이 GitHub 프로젝트에서 관련 비즈니스 도메인을 반영하는 이벤트 기반 소프트웨어 솔루션 개발을 위한 토대를 마련하는 것입니다.
도메인 중심 설계(DDD)
관련 비즈니스 도메인의 프로세스와 규칙을 풍부하게 이해하는 도메인 모델 프로그래밍에 개발을 집중시키는 소프트웨어 개발 접근 방식입니다. "도메인 중심 디자인"이라는 용어는 Eric Evans가 같은 제목의 책에서 만들어냈습니다.
DDD는 복잡한 애플리케이션을 쉽게 생성하는 것을 목표로 하며 다음 세 가지 핵심 원칙에 중점을 둡니다.
Eric Evans의 책에서는 도메인 중심 설계에 대한 몇 가지 일반적인 용어를 정의합니다.
도메인 모델
비즈니스 도메인의 프로세스와 정책을 설명하고 해당 도메인과 관련된 필수 작업을 처리하는 데 사용되는 추상화 시스템입니다.
유비쿼터스 언어
비즈니스 영역의 특정 요소에 대한 단어 및 설명입니다. 오해를 다시 방지하기 위해 모든 팀 구성원은 일반적으로 도메인 전문가가 사용하는 특정 용어를 채택해야 합니다.
제한된 컨텍스트
특정 도메인 모델이 정의되고 적용 가능한 개념적 경계입니다. 이는 일반적으로 하위 시스템이나 작업 분야를 나타냅니다. 이는 주로 언어적 구분이며 각 경계 컨텍스트에는 고유한 유비쿼터스 언어가 있습니다.
예: 사용자가 "고객"이라고 불리는 고객 관리.
Eric Evans의 책은 도메인 모델의 특정 부분을 더욱 차별화합니다. 몇 가지 예를 들면 다음과 같습니다.
실재
속성보다는 ID로 정의되는 객체입니다.
예: 재킷, 머리 색깔 또는 특정 순간에 사용되는 언어의 선택에 관계없이 사람은 항상 같은 사람으로 유지됩니다.
값 개체
속성 값에 의해서만 정의되는 객체입니다. 값 개체는 변경할 수 없으며 고유한 ID가 없습니다. 값 개체는 동일한 속성을 가진 다른 값 개체로 대체될 수 있습니다.
예: 사람에게 초점을 맞출 때 깨진 선글라스는 똑같이 보이는 새 선글라스로 쉽게 교체할 수 있습니다.
골재
단일 트랜잭션 단위로 통합된 하나 이상의 엔터티와 선택적 값 개체의 클러스터입니다. 하나의 엔터티는 Aggregate의 기반을 형성하므로 Aggregate 루트로 선언됩니다. 모든 협력 엔터티 및 값 개체의 속성은 이 단일 기본 엔터티를 통해서만 액세스할 수 있습니다. Aggregate는 항상 일관된 상태에 있어야 합니다. 객체 지향 프로그래밍에서 이는 일반적으로 개인 setter 및 보호 getter를 사용하여 수행됩니다.
예: 자동차 판매 컨텍스트에서 하나의 자동차(엔티티)는 차량 식별 번호로 정의됩니다. 이 자동차에는 4개의 바퀴(값 개체)가 있을 수 있으며 특정 시간이 지나면 교체해야 할 수도 있습니다.
도메인 이벤트
도메인 모델 내 활동의 결과로 생성되는 개체입니다. 이 활동과 관련된 정보를 보유하고 전달하는 데 사용됩니다. 도메인 이벤트는 일반적으로 도메인 전문가가 관련성이 있다고 생각하는 활동에 대해 생성됩니다.
육각형 아키텍처(포트 및 어댑터)
2005년 Alistair Cockburn이 제안한 소프트웨어 설계에 사용되는 아키텍처 패턴입니다. 이 패턴은 높은 수준의 유지 관리성을 달성하는 것을 목표로 하며 애플리케이션을 3개 계층으로 설명합니다. 각 계층은 인터페이스(포트) 및 구현(어댑터)을 사용하여 인접 계층과 통신합니다.
이 아키텍처 패턴의 핵심 규칙은 종속성이 내부만을 가리킬 수 있다는 것입니다. 내부 서클의 어떤 것도 외부 서클의 무언가에 대해 전혀 알 수 없습니다. 외부를 가리키려는 모든 종속성(예: 애플리케이션 계층에서 데이터베이스 호출)은 제어 반전(IoC) 또는 종속성 주입(DI)을 통해 인스턴스화되어야 합니다.
MediatR(미리 구축된 메시징 프레임워크)을 사용하는 CQRS
CQRS는 Command/Query Responsibility Segregation의 약어이며 2010년 Greg Young에 의해 처음 설명되었습니다. CQS(Command Query Separation) 원칙을 기반으로 하며 읽기 및 쓰기 작업을 분리할 수 있습니다. CQS는 다음과 같이 말합니다.
CQS에 비해 CQRS의 개선점은 해당 명령과 쿼리가 메서드가 아닌 모델로 처리된다는 것입니다. 이러한 모델은 한 지점에서 객체로 디스패치될 수 있으며 시스템의 다른 지점에서 필수 핸들러에 의해 처리될 수 있습니다. 각 핸들러는 각 작업을 명확하게 분리하기 위해 응답 모델을 반환합니다.
중재자 패턴을 사용하면 중재자 개체를 활용하여 느슨하게 연결된 명령/쿼리 및 처리기를 구현할 수 있습니다. 객체는 더 이상 서로 직접 통신하지 않고 대신 중재자를 통해 통신합니다.
MediatR 프레임워크는 Jimmy Bogard가 만든 중재자 패턴의 오픈 소스 구현입니다. 이 프로젝트에서는 Framework Layer와 Application Layer 간의 통신을 위해 활용될 것입니다. 또한 Command 데이터베이스에서 Query 데이터베이스로 데이터를 투영하는 데에도 사용됩니다.
이벤트 소싱
도메인에 데이터의 현재 상태만 저장하는 것이 아니라 애플리케이션 상태의 모든 변경 사항을 저장하기 위한 아키텍처 설계 패턴입니다. 이 패턴은 Greg Young에 의해 도입되었으며 이후 수많은 채택이 이루어졌습니다.
패턴은 애플리케이션 상태에 대한 모든 변경 사항을 이벤트 객체로 캡처하려고 합니다. 그런 다음 이러한 이벤트 개체는 발생 순서에 따라 추가 전용 방식으로 저장됩니다. 이를 통해 지금까지 발생한 일련의 이벤트에 대해 객체의 현재 상태를 재현할 수 있을 뿐만 아니라 궁극적으로 시간을 거슬러 올라가 주어진 시간 동안 객체 상태를 재현할 수 있습니다.
은행 계좌는 이벤트 소싱 원칙의 좋은 예가 될 수 있습니다. 돈을 인출하거나 입금할 때마다 현재 잔액만 업데이트하는 것이 아니라 잔액을 업데이트하는 방식으로 변경 금액이 기록됩니다. 그런 다음 일련의 이벤트를 검토하여 매번 인출되거나 입금된 금액에 대한 해당 정보를 통해 현재 잔액을 계산합니다.
이벤트 소싱은 모든 변경 요청과 함께 도메인 모델에 의해 트리거되는 도메인 이벤트를 저장하는 데 매우 적합하기 때문에 도메인 기반 설계와 잘 작동합니다.
이벤트 소싱도 CQRS의 이점을 크게 누릴 수 있습니다. 현재 상태를 다시 생성하기 위해 요청 중인 개체와 관련하여 기록된 모든 이벤트를 조사해야 하는 이벤트 소싱 데이터베이스에 대해 쿼리를 수행할 필요 없이 전용 쿼리 데이터베이스에 대해 이 쿼리를 수행할 수 있습니다. 이 쿼리 데이터베이스는 자체 이벤트 핸들러에 의해 업데이트되어 이벤트 소싱 데이터베이스에 추가된 직후 전달되는 동일한 이벤트를 수신합니다. 이러한 업데이트 프로세스를 투영이라고 합니다.
이러한 데이터베이스 분리는 확장성과 성능 최적화 측면에서 엄청난 잠재력을 발휘할 수 있는 기반을 마련합니다. 애플리케이션 상태에 대한 관련 변경이 발생한 직후 이벤트 소싱 데이터베이스 클라이언트에서 전달되는 이벤트를 해당 이벤트 핸들러가 수신하도록 함으로써 쿼리 데이터베이스의 여러 인스턴스를 생성하고 동기화할 수 있습니다. 쿼리별로 최적화된 데이터베이스 유형과 데이터 비정규화 정도를 선택하면 성능이 크게 향상될 수 있습니다.
읽기 모델의 지속적인 업데이트는 동기식 또는 비동기식으로 발생할 수 있습니다. 후자는 읽기 모델이 작은 시간 간격(보통 밀리초) 동안 쓰기 모델과 동기화되지 않아 최종 일관성을 희생합니다.
공유 커널
제한된 컨텍스트에서 공유되는 공통 도메인 기반 설계 특정 기본 클래스, 도메인 엔터티, 값 개체 등을 포함하는 도메인 계층용 공통 라이브러리입니다.
시작하기
이 프로젝트를 있는 그대로 실행하려면 다음 단계를 따르세요.
전제조건
설정
API의 Swagger 문서를 보려면 브라우저에서 https://localhost:5001/swagger/index.html을 실행하세요.
Swagger, Postman 또는 기타 애플리케이션을 사용하여 https://localhost:5001/api/Administration/Register에 POST 요청을 보내 초기 관리자 계정을 등록하세요. 다음 개체를 보냅니다.
콘솔 애플리케이션을 살펴보거나 애플리케이션 로그에 대해 재구성된 출력을 살펴보세요. 사용자 등록이 성공적으로 완료되면 EmailService에서 제공하는 이메일 확인 링크가 로그에 기록되어야 합니다. 이 URL을 복사하여 브라우저에 붙여넣고 Enter 키를 눌러 등록을 완료하세요. 이메일 서비스의 부적절한 구현을 자유롭게 변경하거나 구축하세요 ;-)
모든 준비가 완료되었습니다. 다음으로 로그인하세요.
EventStoreDB GUI를 보려면 브라우저에서 http://localhost:2113/을 실행하십시오. 저장된 모든 이벤트를 보려면 "스트림 브라우저" 탭을 엽니다.
테스트는 다음을 실행하여 실행할 수 있습니다.
기술
이 프로젝트는 다음 기술/NuGet 패키지를 활용합니다.
자료 / 추천 도서
알베르토 브란돌리니:
https://www.eventstorming.com
본 버논:
https://dddcommunity.org/wp-content/uploads/files/pdfarticles/Vernon2011_1.pdf
https://dddcommunity.org/wp-content/uploads/files/pdfarticles/Vernon2011_2.pdf
https://dddcommunity.org/wp-content/uploads/files/pdfarticles/Vernon2011_3.pdf
알리스테어 콕번:
https://web.archive.org/web/20180822100852/http://alistair.cockburn.us/Hexagonal+architecture
로버트 C. 마틴(밥 삼촌):
https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html
세자르 드 라 토레, 빌 와그너, 마이크 루소스:
https://docs.microsoft.com/en-us/dotnet/architecture/microservices/
그렉 영
https://cqrs.files.wordpress.com/2010/11/cqrs_documents.pdf
https://cqrs.wordpress.com/documents/building-event-storage/
https://msdn.microsoft.com/en-us/library/jj591559.aspx
마틴 파울러:
https://www.martinfowler.com/bliki/CQRS.html
지미 보가드:
https://github.com/jbogard/MediatR
https://www.youtube.com/watch?v=SUiWfhAhgQw
도메인 중심 설계:
https://dddcommunity.org
https://thedomaindrivendesign.io
https://dotnetcodr.com/2013/09/12/a-model-net-web-service-based-on-domain-driven-design-part-1-introduction/
https://dotnetcodr.com/2015/10/22/domain-driven-design-with-web-api-extensions-part-1-notifications/
육각형 아키텍처:
https://fideloper.com/hexagonal-architecture
https://herbertograca.com/2017/09/14/ports-adapters-architecture/
크레딧
http://www.andreavallotti.tech/en/2018/01/event-sourcing-and-cqrs-in-c/
https://www.Exceptionnotfound.net/real-world-cqrs-es-with-asp-net-and-redis-part-1-overview/
https://buildplease.com/pages/fpc-1/
https://dotnetcoretutorials.com/2019/04/30/the-mediator-pattern-in-net-core-part-1-whats-a-mediator/
https://itnext.io/why-and-how-i-implemented-cqrs-and-mediator-patterns-in-a-microservice-b07034592b6d