DDD领域驱动设计总结和C#代码示例
<p data-first-child="" data-pid="_f9a1FIq">DDD(领域驱动设计)是一种软件设计方法,它强调以业务领域为核心来驱动软件的设计和开发。</p><p data-pid="hVl6H82S">DDD 的设计初衷是为了解决复杂业务领域的设计和开发问题,它提供了一套丰富的概念和模式,帮助开发者更好地理解和建模业务领域,从而提高软件的质量和可维护性。</p>
<h2>一、DDD主要组成</h2>
<p data-pid="bUZ1xppV">DDD 的主要模式包括实体(Entity)、值对象(Value Object)、聚合(Aggregate)、领域服务(Domain Service)、应用服务(Application Service)和领域事件(Domain Event)等。这些模式共同构成了一个完整的领域模型,用于指导软件系统的开发。</p>
<h3>实体(Entity)</h3>
<p data-pid="O64irSz3">实体是具有唯一标识的领域对象,它的状态可以随时间改变。实体的标识与它的属性状态无关,即使对象的所有属性值都改变了,实体的标识仍然保持不变。实体封装了业务逻辑,并且可以通过它的业务逻辑来修改其状态。</p>
<h3>值对象(Value Object)</h3>
<p data-pid="ID7ttLNx">值对象表示没有独立存在意义的领域概念,它只有通过与其他对象的关联才有意义。值对象没有唯一标识,它们的相等性是通过属性值来判定的。值对象通常是不可变的,这意味着一旦创建,它们的内部状态就不能被改变。</p>
<h3>聚合(Aggregate)</h3>
<p data-pid="tTYD2e-T">聚合是一组不能独立存在的实体和值对象的集合,它们一起作为数据修改和持久化的基本单元。聚合由一个聚合根(通常是实体)管理,聚合根负责维护聚合的一致性和完整性。外部对象不能直接修改聚合内部的实体和值对象,只能通过聚合根来进行。</p>
<h3>领域服务(Domain Service)</h3>
<p data-pid="LJkcAi8l">领域服务是领域逻辑的一部分,但它不属于任何实体或值对象。领域服务通常用于实现领域对象之间的业务逻辑,如两个实体之间的计算或转换。领域服务是无状态的,它只依赖于输入的参数来执行操作。</p>
<h3>应用服务(Application Service)</h3>
<p data-pid="YcsBtJIB">应用服务是与领域模型交互的入口点,它属于应用层。应用服务处理应用程序的工作流程,协调领域对象来执行用例,并最终引发领域事件。应用服务通常作为API或用户界面与外部世界交互。</p>
<h3>领域事件(Domain Event)</h3>
<p data-pid="duBZLxH6">领域事件表示在领域中发生的业务事件,它封装了事件的信息,并可以触发后续的业务逻辑。领域事件是DDD中实现事件驱动架构的关键部分,它允许系统对业务事件做出响应,实现业务逻辑的解耦。</p>
<h3>反腐败层(Anti-Corruption Layer)</h3>
<p data-pid="bL6gYaxw">反腐败层是应用层的一部分,用于保护领域模型不受外部模型的侵蚀。当外部系统或旧系统集成到新系统时,反腐败层确保外部模型不会破坏领域模型的一致性和清晰性。</p>
<h3>限界上下文(Bounded Context)</h3>
<p data-pid="AgPJA62n">限界上下文定义了模型的边界,在边界内部模型是一致的,而不同限界上下文之间的模型可能不同。限界上下文帮助团队划分问题域,实现团队间的有效沟通和协作。</p>
<h3>持续集成(Continuous Integration)</h3>
<p data-pid="fiJeKTgC">DDD强调持续集成,领域模型会随着业务需求的变化而演进。团队成员需要频繁地集成他们的工作,以确保模型的一致性和整体性。</p>
<h2>二、应用场景</h2>
<p data-pid="XTGNhGQt">DDD 特别适合于以下应用场景:</p>
<ol>
<li data-pid="rzkksvq7">复杂的业务领域:当业务逻辑非常复杂,需要高度定制化的解决方案时。</li>
<li data-pid="rlfHIwex">持续演进的业务需求:DDD 支持快速迭代和演进,适应不断变化的业务需求。</li>
<li data-pid="dKYiAZze">需要高度可维护性:通过将业务逻辑集中在领域模型中,DDD 提高了系统的可维护性。</li>
<li data-pid="0QDPWbvq">分布式系统:DDD 与微服务架构天然契合,适合构建分布式系统。</li>
</ol>
<h2>三、代码示例</h2>
<p data-pid="9jdBLO9d">以下是一个简单的DDD风格的C#代码示例,包括实体、聚合根、领域服务和领域事件。</p>
<p data-pid="jzQouzIx">实体(Entity)</p>
<div class="highlight">
<pre><code class="language-csharp"><span class="k">public <span class="k">class <span class="nc">Student <span class="p">: <span class="n">Entity
<span class="p">{
<span class="k">public <span class="n">Student<span class="p">(<span class="n">Guid <span class="n">id<span class="p">, <span class="kt">string <span class="n">name<span class="p">, <span class="kt">string <span class="n">email<span class="p">)
<span class="p">{
<span class="n">Id <span class="p">= <span class="n">id<span class="p">;
<span class="n">Name <span class="p">= <span class="n">name<span class="p">;
<span class="n">Email <span class="p">= <span class="n">email<span class="p">;
<span class="p">}
<span class="k">public <span class="n">Guid <span class="n">Id <span class="p">{ <span class="k">get<span class="p">; <span class="k">private <span class="k">set<span class="p">; <span class="p">}
<span class="k">public <span class="kt">string <span class="n">Name <span class="p">{ <span class="k">get<span class="p">; <span class="k">private <span class="k">set<span class="p">; <span class="p">}
<span class="k">public <span class="kt">string <span class="n">Email <span class="p">{ <span class="k">get<span class="p">; <span class="k">private <span class="k">set<span class="p">; <span class="p">}
<span class="c1">// 实体的业务逻辑方法
<span class="c1"> <span class="k">public <span class="k">void <span class="n">UpdateEmail<span class="p">(<span class="kt">string <span class="n">newEmail<span class="p">)
<span class="p">{
<span class="n">Email <span class="p">= <span class="n">newEmail<span class="p">;
<span class="p">}
<span class="p">}
<span class="k">public <span class="k">abstract <span class="k">class <span class="nc">Entity
<span class="p">{
<span class="k">public <span class="n">Guid <span class="n">Id <span class="p">{ <span class="k">get<span class="p">; <span class="k">protected <span class="k">set<span class="p">; <span class="p">}
<span class="p">}
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</div>
<p data-pid="9xITm5VE">值对象(Value Object)</p>
<div class="highlight">
<pre><code class="language-csharp"><span class="na">
<span class="k">public <span class="k">class <span class="nc">Address
<span class="p">{
<span class="k">public <span class="kt">string <span class="n">Street <span class="p">{ <span class="k">get<span class="p">; <span class="k">private <span class="k">set<span class="p">; <span class="p">}
<span class="k">public <span class="kt">string <span class="n">City <span class="p">{ <span class="k">get<span class="p">; <span class="k">private <span class="k">set<span class="p">; <span class="p">}
<span class="k">public <span class="n">Address<span class="p">(<span class="kt">string <span class="n">street<span class="p">, <span class="kt">string <span class="n">city<span class="p">)
<span class="p">{
<span class="n">Street <span class="p">= <span class="n">street<span class="p">;
<span class="n">City <span class="p">= <span class="n">city<span class="p">;
<span class="p">}
<span class="c1">// ValueObject 需要重写 Equals 和 GetHashCode
<span class="c1"> <span class="k">public <span class="k">override <span class="kt">bool <span class="n">Equals<span class="p">(<span class="kt">object <span class="n">obj<span class="p">)
<span class="p">{
<span class="c1">// 实现细节...
<span class="c1"> <span class="p">}
<span class="k">public <span class="k">override <span class="kt">int <span class="n">GetHashCode<span class="p">()
<span class="p">{
<span class="c1">// 实现细节...
<span class="c1"> <span class="p">}
<span class="p">}
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</div>
<p data-pid="LHx_9u0C">聚合根(Aggregate Root)</p>
<div class="highlight">
<pre><code class="language-csharp"><span class="k">public <span class="k">class <span class="nc">School <span class="p">: <span class="n">AggregateRoot
<span class="p">{
<span class="k">private <span class="n">List<span class="p"><<span class="n">Student<span class="p">> <span class="n">_students <span class="p">= <span class="k">new <span class="n">List<span class="p"><<span class="n">Student<span class="p">>();
<span class="k">public <span class="k">void <span class="n">EnrollStudent<span class="p">(<span class="n">Student <span class="n">student<span class="p">)
<span class="p">{
<span class="n">_students<span class="p">.<span class="n">Add<span class="p">(<span class="n">student<span class="p">);
<span class="c1">// 触发领域事件
<span class="c1"> <span class="n">Publish<span class="p">(<span class="k">new <span class="n">StudentEnrolledEvent<span class="p">(<span class="n">student<span class="p">.<span class="n">Id<span class="p">, <span class="n">student<span class="p">.<span class="n">Name<span class="p">));
<span class="p">}
<span class="c1">// 领域事件发布
<span class="c1"> <span class="k">private <span class="k">void <span class="n">Publish<span class="p">(<span class="n">IEvent <span class="n">@event<span class="p">)
<span class="p">{
<span class="c1">// 发布事件到事件总线或存储系统
<span class="c1"> <span class="p">}
<span class="p">}
<span class="k">public <span class="k">abstract <span class="k">class <span class="nc">AggregateRoot
<span class="p">{
<span class="k">public <span class="n">Guid <span class="n">Id <span class="p">{ <span class="k">get<span class="p">; <span class="k">protected <span class="k">set<span class="p">; <span class="p">}
<span class="p">}
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</div>
<p data-pid="QEW7kjFg">领域服务(Domain Service)</p>
<div class="highlight">
<pre><code class="language-csharp"><span class="k">public <span class="k">class <span class="nc">SchoolDomainService
<span class="p">{
<span class="k">public <span class="k">void <span class="n">CreateSchool<span class="p">(<span class="n">School <span class="n">school<span class="p">)
<span class="p">{
<span class="c1">// 执行创建学校的业务逻辑
<span class="c1"> <span class="p">}
<span class="p">}
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</div>
<p data-pid="IGfl0gTW">领域事件(Domain Event)</p>
<div class="highlight">
<pre><code class="language-csharp"><span class="k">public <span class="k">class <span class="nc">StudentEnrolledEvent <span class="p">: <span class="n">IEvent
<span class="p">{
<span class="k">public <span class="n">Guid <span class="n">StudentId <span class="p">{ <span class="k">get<span class="p">; <span class="k">private <span class="k">set<span class="p">; <span class="p">}
<span class="k">public <span class="kt">string <span class="n">StudentName <span class="p">{ <span class="k">get<span class="p">; <span class="k">private <span class="k">set<span class="p">; <span class="p">}
<span class="k">public <span class="n">StudentEnrolledEvent<span class="p">(<span class="n">Guid <span class="n">studentId<span class="p">, <span class="kt">string <span class="n">studentName<span class="p">)
<span class="p">{
<span class="n">StudentId <span class="p">= <span class="n">studentId<span class="p">;
<span class="n">StudentName <span class="p">= <span class="n">studentName<span class="p">;
<span class="p">}
<span class="p">}
<span class="k">public <span class="k">interface <span class="n">IEvent
<span class="p">{
<span class="c1">// 事件接口定义
<span class="c1"><span class="p">}
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</div>
<h3>反腐败层(ACL)</h3>
<p data-pid="3VJf0bmY">假设我们有一个外部系统,其学生信息的表示与我们的领域模型不同。我们需要创建一个反腐败层来转换外部系统的学生信息为我们的<code>Student</code>实体。</p>
<div class="highlight">
<pre><code class="language-csharp"><span class="k">public <span class="k">class <span class="nc">ExternalStudentDTO
<span class="p">{
<span class="c1">// 外部系统的学生信息
<span class="c1"> <span class="k">public <span class="n">Guid <span class="n">Id <span class="p">{ <span class="k">get<span class="p">; <span class="k">set<span class="p">; <span class="p">}
<span class="k">public <span class="kt">string <span class="n">Name <span class="p">{ <span class="k">get<span class="p">; <span class="k">set<span class="p">; <span class="p">}
<span class="k">public <span class="kt">string <span class="n">Email <span class="p">{ <span class="k">get<span class="p">; <span class="k">set<span class="p">; <span class="p">}
<span class="c1">// ... 其他属性
<span class="c1"><span class="p">}
<span class="k">public <span class="k">class <span class="nc">StudentFactory
<span class="p">{
<span class="k">public <span class="k">static <span class="n">Student <span class="n">CreateFromExternalDTO<span class="p">(<span class="n">ExternalStudentDTO <span class="n">externalStudent<span class="p">)
<span class="p">{
<span class="c1">// 转换外部系统的学生信息为内部Student实体
<span class="c1"> <span class="k">return <span class="k">new <span class="n">Student<span class="p">(<span class="n">externalStudent<span class="p">.<span class="n">Id<span class="p">, <span class="n">externalStudent<span class="p">.<span class="n">Name<span class="p">, <span class="n">externalStudent<span class="p">.<span class="n">Email<span class="p">);
<span class="p">}
<span class="p">}
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</div>
<h3>领域事件总线</h3>
<p data-pid="bBFS2uy-">领域事件总线负责协调事件的发布和订阅。</p>
<div class="highlight">
<pre><code class="language-csharp"><span class="k">public <span class="k">class <span class="nc">EventBus
<span class="p">{
<span class="k">private <span class="k">readonly <span class="n">List<span class="p"><<span class="n">IEventHandler<span class="p">> <span class="n">_handlers <span class="p">= <span class="k">new <span class="n">List<span class="p"><<span class="n">IEventHandler<span class="p">>();
<span class="k">public <span class="k">void <span class="n">Subscribe<span class="p">(<span class="n">IEventHandler <span class="n">handler<span class="p">)
<span class="p">{
<span class="n">_handlers<span class="p">.<span class="n">Add<span class="p">(<span class="n">handler<span class="p">);
<span class="p">}
<span class="k">public <span class="k">void <span class="n">Unsubscribe<span class="p">(<span class="n">IEventHandler <span class="n">handler<span class="p">)
<span class="p">{
<span class="n">_handlers<span class="p">.<span class="n">Remove<span class="p">(<span class="n">handler<span class="p">);
<span class="p">}
<span class="k">public <span class="k">void <span class="n">Publish<span class="p">(<span class="kt">object <span class="n">eventToPublish<span class="p">)
<span class="p">{
<span class="k">foreach <span class="p">(<span class="kt">var <span class="n">handler <span class="k">in <span class="n">_handlers<span class="p">)
<span class="p">{
<span class="k">if <span class="p">(<span class="n">handler<span class="p">.<span class="n">CanHandle<span class="p">(<span class="n">eventToPublish<span class="p">))
<span class="p">{
<span class="n">handler<span class="p">.<span class="n">Handle<span class="p">(<span class="n">eventToPublish<span class="p">);
<span class="p">}
<span class="p">}
<span class="p">}
<span class="p">}
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</div>
<h3>应用服务</h3>
<p data-pid="u0KRQLlH">应用服务将处理应用程序的工作流程,调用领域服务,并触发领域事件。</p>
<div class="highlight">
<pre><code class="language-csharp"><span class="k">public <span class="k">class <span class="nc">SchoolApplicationService
<span class="p">{
<span class="k">private <span class="k">readonly <span class="n">School <span class="n">_school<span class="p">;
<span class="k">private <span class="k">readonly <span class="n">EventBus <span class="n">_eventBus<span class="p">;
<span class="k">public <span class="n">SchoolApplicationService<span class="p">(<span class="n">School <span class="n">school<span class="p">, <span class="n">EventBus <span class="n">eventBus<span class="p">)
<span class="p">{
<span class="n">_school <span class="p">= <span class="n">school<span class="p">;
<span class="n">_eventBus <span class="p">= <span class="n">eventBus<span class="p">;
<span class="p">}
<span class="k">public <span class="k">void <span class="n">EnrollStudent<span class="p">(<span class="n">ExternalStudentDTO <span class="n">externalStudent<span class="p">)
<span class="p">{
<span class="c1">// 使用反腐败层转换外部系统的学生信息
<span class="c1"> <span class="kt">var <span class="n">student <span class="p">= <span class="n">StudentFactory<span class="p">.<span class="n">CreateFromExternalDTO<span class="p">(<span class="n">externalStudent<span class="p">);
<span class="n">_school<span class="p">.<span class="n">EnrollStudent<span class="p">(<span class="n">student<span class="p">);
<span class="c1">// 学生注册成功后,通过事件总线发布事件
<span class="c1"> <span class="n">_eventBus<span class="p">.<span class="n">Publish<span class="p">(<span class="k">new <span class="n">StudentEnrolledEvent<span class="p">(<span class="n">student<span class="p">.<span class="n">Id<span class="p">, <span class="n">student<span class="p">.<span class="n">Name<span class="p">));
<span class="p">}
<span class="p">}
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</div>
<h3>领域服务</h3>
<p data-pid="JTuKA1aQ">领域服务包含特定领域的业务逻辑,可以被应用服务或领域事件处理器调用。</p>
<div class="highlight">
<pre><code class="language-csharp"><span class="k">public <span class="k">class <span class="nc">SchoolDomainService
<span class="p">{
<span class="c1">// 领域服务中的业务逻辑,例如创建学校等
<span class="c1"> <span class="k">public <span class="k">void <span class="n">CreateSchool<span class="p">(<span class="kt">string <span class="n">schoolName<span class="p">)
<span class="p">{
<span class="c1">// 创建学校的业务逻辑
<span class="c1"> <span class="p">}
<span class="p">}
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</div>
<h3>领域事件处理器</h3>
<p data-pid="LWM23p9p">领域事件处理器响应领域事件,执行相应的操作。</p>
<div class="highlight">
<pre><code class="language-csharp"><span class="k">public <span class="k">class <span class="nc">StudentEnrolledEventHandler <span class="p">: <span class="n">IEventHandler<span class="p"><<span class="n">StudentEnrolledEvent<span class="p">>
<span class="p">{
<span class="k">public <span class="k">void <span class="n">Handle<span class="p">(<span class="n">StudentEnrolledEvent <span class="n">eventToHandle<span class="p">)
<span class="p">{
<span class="c1">// 处理学生注册事件,例如发送欢迎邮件等
<span class="c1"> <span class="p">}
<span class="k">public <span class="kt">bool <span class="n">CanHandle<span class="p">(<span class="kt">object <span class="n">eventToHandle<span class="p">)
<span class="p">{
<span class="k">return <span class="n">eventToHandle <span class="k">is <span class="n">StudentEnrolledEvent<span class="p">;
<span class="p">}
<span class="p">}
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</div>
<p data-pid="Qgc_H77X">在这个示例中,<code>Student</code> 是一个实体,具有唯一标识和业务逻辑。<code>Address</code> 是一个值对象,表示学生地址,它没有唯一标识,是不可变的。<code>School</code> 是聚合根,它包含了多个 <code>Student</code> 对象,并且可以触发领域事件。<code>SchoolDomainService</code> 是领域服务,封装了创建学校的业务逻辑。<code>StudentEnrolledEvent</code> 是领域事件,表示学生注册的事件。</p>
<p data-pid="9bavbdLO">同时我们创建了一个<code>StudentFactory</code>作为反腐败层,用于将外部系统的学生信息转换为内部<code>Student</code>实体。<code>EventBus</code>作为领域事件总线,负责事件的发布和订阅。<code>SchoolApplicationService</code>作为一个应用服务,处理应用程序的工作流程,调用领域服务,并触发领域事件。<code>SchoolDomainService</code>是领域服务,包含创建学校的业务逻辑。最后,我们实现了一个<code>StudentEnrolledEventHandler</code>来响应<code>StudentEnrolledEvent</code>。</p>
<p data-pid="4F00pWmf">这些组件共同协作,形成了一个完整的DDD应用示例,展示了如何在C#中实现DDD的各种模式和实践。</p>
<p data-pid="4F00pWmf"> </p>
<p data-pid="4F00pWmf">周国庆</p>
<p data-pid="4F00pWmf">20240922</p><br><br>
来源:https://www.cnblogs.com/tianqing/p/18148759
頁:
[1]