1 前置阅读

在阅读本文章之前,你可以先阅读:

  • DDD领域驱动设计是什么
  • DDD领域驱动设计:实体、值对象、聚合根
  • DDD领域驱动设计:仓储
  • MediatR一个优秀的.NET中介者框架

2 什么是领域事件?

领域事件是在领域中发生的事,你希望同一个领域(进程)的其他部分了解它。 通知部分通常以某种方式对事件作出反应。

3 实现领域事件?

重点强调领域事件发布/订阅是使用 MediatR 同步实现的。

首先,定义待办事项已更新的领域事件

public class TodoUpdatedDomainEvent : INotification
{
public Todo Todo { get; } public TodoUpdatedDomainEvent(Todo todo)
{
Todo = todo;
}
}

然后,引发领域事件,将域事件添加到集合,然后在提交事务之前或之后立即调度这些域事件,而不是立即调度到域事件处理程序 。

public abstract class Entity
{
//...
private List<INotification> domainEvents;
public IReadOnlyCollection<INotification> DomainEvents => domainEvents?.AsReadOnly(); public void AddDomainEvent(INotification eventItem)
{
domainEvents = domainEvents ?? new List<INotification>();
domainEvents.Add(eventItem);
} public void RemoveDomainEvent(INotification eventItem)
{
domainEvents?.Remove(eventItem);
} public void ClearDomainEvents()
{
domainEvents?.Clear();
}
//... 其他代码
}

要引发事件时,只需将其在聚合根实体的方法处添加到代码中的事件集合。

public class Todo : AggregateRoot
{
//...
public void Update(
string name)
{
Name = name;
AddDomainEvent(new TodoUpdatedDomainEvent(this));
}
//... 其他代码
}

请注意 AddDomainEvent 方法的唯一功能是将事件添加到列表。 尚未调度任何事件,尚未调用任何事件处理程序。你需要在稍后将事务提交到数据库时调度事件。

public class Repository : IDisposable, IRepository
{
//...
private readonly IMediator mediator;
private readonly DbContext context; public Repository(DbContext context, IMediator mediator)
{
this.context = context ?? throw new ArgumentNullException(nameof(context));
this.mediator = mediator ?? throw new ArgumentNullException(nameof(mediator));
} public void Save()
{
mediator.DispatchDomainEvents(context);
context.SaveChanges();
} public static void DispatchDomainEvents(this IMediator mediator, DbContext ctx)
{
var domainEntities = ctx.ChangeTracker
.Entries<Entity>()
.Where(x => x.Entity.DomainEvents != null && x.Entity.DomainEvents.Any()); var domainEvents = domainEntities
.SelectMany(x => x.Entity.DomainEvents)
.ToList(); domainEntities.ToList()
.ForEach(entity => entity.Entity.ClearDomainEvents()); foreach (var domainEvent in domainEvents)
mediator.Publish(domainEvent);
}
//... 其他代码
}

最后,订阅并处理领域事件

public class TodoUpdatedDomainEventHandler : INotificationHandler<TodoUpdatedDomainEvent>
{
private readonly ILoggerFactory logger;
public TodoUpdatedDomainEventHandler(ILoggerFactory logger)
{
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
} public Task Handle(TodoUpdatedDomainEvent todoUpdatedDomainEvent, CancellationToken cancellationToken)
{
logger.CreateLogger<TodoUpdatedDomainEvent>().LogDebug("Todo with Id: {TodoId} has been successfully updated",
todoUpdatedDomainEvent.Todo.Id);
return Task.CompletedTask;
}
}

DDD领域驱动设计:领域事件的更多相关文章

  1. 领域驱动设计和Spring

    原文 http://static.olivergierke.de/lectures/ddd-and-spring/ 1.介绍这篇文章是的介绍一下领域驱动设计的基础构件.概念和Java的web应用(主要 ...

  2. 如何使用ABP进行软件开发(2) 领域驱动设计和三层架构的对比

    简述 上一篇简述了ABP框架中的一些基础理论,包括ABP前后端项目的分层结构,以及后端项目中涉及到的知识点,例如DTO,应用服务层,整洁架构,领域对象(如实体,聚合,值对象)等. 笔者也曾经提到,AB ...

  3. 基于“事件”驱动的领域驱动设计(DDD)框架分析

    摘抄自 从去年10月份开始,学了几个月的领域驱动设计(Domain Driven Design,简称DDD).主要是学习领域驱动设计之父Eric Evans的名著:<Domain-driven ...

  4. 浅谈我对DDD领域驱动设计的理解

    从遇到问题开始 当人们要做一个软件系统时,一般总是因为遇到了什么问题,然后希望通过一个软件系统来解决. 比如,我是一家企业,然后我觉得我现在线下销售自己的产品还不够,我希望能够在线上也能销售自己的产品 ...

  5. DDD 领域驱动设计-商品建模之路

    最近在做电商业务中,有关商品业务改版的一些东西,后端的架构设计采用现在很流行的微服务,有关微服务的简单概念: 微服务是一种架构风格,一个大型复杂软件应用由一个或多个微服务组成.系统中的各个微服务可被独 ...

  6. DDD 领域驱动设计-如何控制业务流程?

    上一篇:<DDD 领域驱动设计-如何完善 Domain Model(领域模型)?> 开源地址:https://github.com/yuezhongxin/CNBlogs.Apply.Sa ...

  7. [转]DDD领域驱动设计基本理论知识总结

    领域驱动设计之领域模型 加一个导航,关于如何设计聚合的详细思考,见这篇文章. 2004年Eric Evans 发表Domain-Driven Design –Tackling Complexity i ...

  8. 领域驱动设计(DDD)部分核心概念的个人理解

    领域驱动设计(DDD)是一种基于模型驱动的软件设计方式.它以领域为核心,分析领域中的问题,通过建立一个领域模型来有效的解决领域中的核心的复杂问题.Eric Ivans为领域驱动设计提出了大量的最佳实践 ...

  9. DDD领域驱动设计之领域服务

    1.DDD领域驱动设计实践篇之如何提取模型 2.DDD领域驱动设计之聚合.实体.值对象 3.DDD领域驱动设计之领域基础设施层 什么是领域服务,DDD书中是说,有些类或者方法,放实体A也不好,放实体B ...

随机推荐

  1. JAVA读取EXCEL_自动生成实体类

    代码实现PropertyAnno.java import java.lang.annotation.ElementType; import java.lang.annotation.Retention ...

  2. mysql多个TimeStamp设置

    mysql多个TimeStamp设置 2012-11-02 12:58  轩脉刃  阅读(39590)  评论(3)  编辑  收藏 timestamp设置默认值是Default CURRENT_TI ...

  3. java字符统计+字符串压缩

    要实习了.突然发现自己好像什么都不会,就去看看题吧.在网上看到一个字符串压缩的题.看了一眼,感觉用python很简单.一个for循环+字典就可以搞定. 但是呢,主要还是java.下面就用java来实现 ...

  4. JAVA NIO Selector Channel

    These four events are represented by the four SelectionKey constants: SelectionKey.OP_CONNECT Select ...

  5. JS常见面试题,看看你都会多少?

    1. 如何在ES5环境下实现let 这个问题实质上是在回答let和var有什么区别,对于这个问题,我们可以直接查看babel转换前后的结果,看一下在循环中通过let定义的变量是如何解决变量提升的问题 ...

  6. kafka的概念

    1.生产者: 生产者发送消息到broker,有三种确认方式(request.required.acks)acks = 0: producer不会等待broker(leader)发送ack .因为发送消 ...

  7. C# 设置默认关联程序

    以下代码做个Mark /// <summary> /// Create an associaten for a file extension in the windows registry ...

  8. Tomcat配置上遇到的一些问题

    Tomcat启动:在bin目录下双击startup.bat文件就行. 访问:在浏览器输入http://localhost:8080 回车访问的是自己 的界面: http://othersip:8080 ...

  9. Count PAT's (25) PAT甲级真题

    题目分析: 由于本题字符串长度有10^5所以直接暴力是不可取的,猜测最后的算法应该是先预处理一下再走一层循环就能得到答案,所以本题的关键就在于这个预处理的过程,由于本题字符串匹配的内容的固定的PAT, ...

  10. mysql中的基本注入函数

    1. 常见数据库注入函数: MYSQL: and length((user))>10 ACCESS: and (select count() from MSysAccessObject)> ...