DDD实践:领域事件
要求:修改good表,添加 organization
基础定义
用于引发和调度事件的延迟方法 AddDomainEvent
Domain\SeedWork\Entity.cs
public abstract class Entity<T>
{
int? _requestedHashCode;
T _Id;
public virtual T Id
{
get
{
return _Id;
}
protected set
{
_Id = value;
}
}
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 bool IsTransient()
{
return Id.ToString() == default(T).ToString();
}
public override bool Equals(object obj)
{
if (obj == null || !(obj is Entity<T>))
return false;
if (Object.ReferenceEquals(this, obj))
return true;
if (this.GetType() != obj.GetType())
return false;
Entity<T> item = (Entity<T>)obj;
if (item.IsTransient() || this.IsTransient())
return false;
else
return item.Id.ToString() == this.Id.ToString();
}
public override int GetHashCode()
{
if (!IsTransient())
{
if (!_requestedHashCode.HasValue)
_requestedHashCode = this.Id.GetHashCode() ^ 31; // XOR for random distribution (http://blogs.msdn.com/b/ericlippert/archive/2011/02/28/guidelines-and-rules-for-gethashcode.aspx)
return _requestedHashCode.Value;
}
else
return base.GetHashCode();
}
public static bool operator ==(Entity<T> left, Entity<T> right)
{
if (Object.Equals(left, null))
return (Object.Equals(right, null)) ? true : false;
else
return left.Equals(right);
}
public static bool operator !=(Entity<T> left, Entity<T> right)
{
return !(left == right);
}
}
聚合根
Goods.cs
public class Goods: Entity<Guid>, IAggregateRoot
{
public string CreatedBy { get; private set; }
public DateTime? CreatedTime { get; private set; }
public string ModifiedBy { get; private set; }
public DateTime? ModifiedTime { get; private set; }
public string GoodsName { get; private set; }
public int? GoodsNum { get; private set; }
public Goods(string createdBy, DateTime? createdTime, string modifiedBy, DateTime? modifiedTime, string goodsName, int? goodsNum)
{
CreatedBy = createdBy;
CreatedTime = createdTime;
ModifiedBy = modifiedBy;
ModifiedTime = modifiedTime;
GoodsName = goodsName;
GoodsNum = goodsNum;
}
public void StockAddedToGoods()
{
AddDomainEvent(new StockAddedToGoodsDomainEvent()); //加入事件列表
}
public void SetModified()
{
ModifiedBy = "aaaaa";
ModifiedTime = DateTime.Now;
}
}
请注意 AddDomainEvent 方法的唯一功能是将事件添加到列表。 尚未调度任何事件,尚未调用任何事件处理程序。
你需要在稍后将事务提交到数据库时调度事件。 如果使用 Entity Framework Core,意味着在 EF DbContext 的 SaveChanges 方法中,如以下示例所示:
// EF Core DbContext
public class SampleContext : DbContext, IUnitOfWork
{
// ...
public async Task<bool> SaveEntitiesAsync(CancellationToken cancellationToken = default(CancellationToken))
{
// Dispatch Domain Events collection.
// Choices:
// A) Right BEFORE committing data (EF SaveChanges) into the DB. This makes
// a single transaction including side effects from the domain event
// handlers that are using the same DbContext with Scope lifetime
// B) Right AFTER committing data (EF SaveChanges) into the DB. This makes
// multiple transactions. You will need to handle eventual consistency and
// compensatory actions in case of failures.
await _mediator.DispatchDomainEventsAsync(this);
// After this line runs, all the changes (from the Command Handler and Domain
// event handlers) performed through the DbContext will be committed
var result = await base.SaveChangesAsync();
}
}
Organization.cs
public class Organization:
Entity<Guid>, IAggregateRoot
{
public string CreatedBy { get; set; }
public DateTime CreatedTime { get; set; }
public string ModifiedBy { get; set; }
public DateTime ModifiedTime { get; set; }
public string Name { get; set; }
private Organization() { }
public Organization(Guid id, string name, string createdBy, DateTime createdTime, string modifiedBy,DateTime modifiedTime)
{
Id = id;
Name = name;
CreatedBy = createdBy;
ModifiedBy = modifiedBy;
ModifiedTime = modifiedTime;
AddDomainEvent(new OrganizationCreatedDomainEvent(name, id));
}
}
CQRS

1.创建命令
[DataContract]
public class UpdateGoodsAndAddStockCommand : IRequest<bool>
{
[DataMember]
public Guid Id { get; set; }
[DataMember]
public string CreatedBy { get; private set; }
[DataMember]
public string GoodsName { get; private set; }
[DataMember]
public int GoodsNum { get; private set; }
}
2.创建处理
public class UpdateGoodsAndAddStockCommandHandler: IRequestHandler<UpdateGoodsAndAddStockCommand, bool>
{
private readonly IGoodsRepository _goodsRepository;
private readonly IMediator _mediator;
public UpdateGoodsAndAddStockCommandHandler(IGoodsRepository goodsRepository, IMediator mediator)
{
_goodsRepository = goodsRepository;
_mediator = mediator;
}
public async Task<bool> Handle(UpdateGoodsAndAddStockCommand request, CancellationToken cancellationToken)
{
var good = await _goodsRepository.GetAsync(request.Id);
good.SetModified(); //赋值修改人和修改时间,
good.StockAddedToGoods(); //添加 Stock 事件
return await _goodsRepository.UnitOfWork.SaveEntitiesAsync();
}
}
3.使用 IoC 的域事件调度程序
public class MediatorModule : Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterAssemblyTypes(typeof(IMediator).GetTypeInfo().Assembly)
.AsImplementedInterfaces();
// Register all the Command classes (they implement IRequestHandler) in assembly holding the Commands
builder.RegisterAssemblyTypes(typeof(CreateOrderCommand).GetTypeInfo().Assembly)
.AsClosedTypesOf(typeof(IRequestHandler<,>));
//这里
builder.RegisterAssemblyTypes(typeof(UpdateGoodsAndAddStockCommand).GetTypeInfo().Assembly)
.AsClosedTypesOf(typeof(IRequestHandler<,>))
// Other registrations ...
}
}

事件列表

Sample.Domain\Event\StockAddedToGoodsDomainEvent.cs
public class StockAddedToGoodsDomainEvent: INotification
{
public Organization Organization { get; }
public StockAddedToGoodsDomainEvent()
{
Organization = new Organization(Guid.NewGuid(), "tss","tss", DateTime.Now,null,DateTime.Now);
}
}
事件处理:StockAddedToGoodsDomainEventHandler
public class StockAddedToGoodsDomainEventHandler : INotificationHandler<StockAddedToGoodsDomainEvent>
{
private readonly IOrganizationRepository _iorganizationRepository;
private readonly ILoggerFactory _logger;
private readonly ISampleIntegrationEventService _sampleIntegrationEventService;
public StockAddedToGoodsDomainEventHandler(IOrganizationRepository iorganizationRepository, ILoggerFactory logger)
{
_iorganizationRepository = iorganizationRepository;
_logger = logger;
}
public async Task Handle(Domain.Events.StockAddedToGoodsDomainEvent notification, CancellationToken cancellationToken)
{
_iorganizationRepository.Add(notification.Organization);
var organization = await _iorganizationRepository.GetAsync(notification.Organization.Id);
await _iorganizationRepository.UnitOfWork.SaveEntitiesAsync();
_logger.CreateLogger(nameof(StockAddedToGoodsDomainEventHandler))
.LogTrace($"Stock with Id: {organization.Id} has been successfully Created");
}
}
资料:
microsoft.doc 域事件:设计和实现
微软的官方.NET CORE微服务示例eShopOnContainers
DDD实践:领域事件的更多相关文章
- Lind.DDD.Events领域事件介绍
回到目录 闲话多说 领域事件大叔感觉是最不好讲的一篇文章,所以拖欠了很久,但最终还是在2015年年前(阴历)把这个知识点讲一下,事件这个东西早在C#1.0时代就有了,那时学起来也是一个费劲,什么是委托 ...
- Lind.DDD敏捷领域驱动框架~介绍
回到占占推荐博客索引 最近觉得自己的框架过于复杂,在实现开发使用中有些不爽,自己的朋友们也经常和我说,框架太麻烦了,要引用的类库太多:之前架构之所以这样设计,完全出于对职责分离和代码附复用的考虑,主要 ...
- ABP入门系列(19)——使用领域事件
ABP入门系列目录--学习Abp框架之实操演练 源码路径:Github-LearningMpaAbp 1.引言 最近刚学习了下DDD中领域事件的理论知识,总的来说领域事件主要有两个作用,一是解耦,二是 ...
- 领域驱动模型DDD(二)——领域事件的订阅/发布实践
前言 凭良心来说,<微服务架构设计模式>此书什么都好,就是选用的业务过于庞大而导致代码连贯性太差,我作为读者来说对于其中采用的自研框架看起来味同嚼蜡,需要花费的学习成本实在是过于庞大,不仅 ...
- DDD~领域事件中使用分布式事务
回到目录 对于一个聚合来说,它可能会被附加很多事件,这里我们叫它领域事务,因为一个聚会我们可以把它理解成一个领域,一个业务.对于领域事件不清楚的同学可以看看我的这篇文章<DDD~领域事件与事件总 ...
- 【DDD】领域驱动设计实践 —— 框架实现
本文主要了在社区服务系统(ECO)中基于SpringMVC+mybatis框架对DDD的落地实现.本文为系列文章中的其中一篇,其他内容可参考:通过业务系统的重构实践DDD. 框架实现图 该框架实现基本 ...
- 【DDD】领域驱动设计实践 —— Domain层实现
本文是DDD框架实现讲解的第三篇,主要介绍了DDD的Domain层的实现,详细讲解了entity.value object.domain event.domain service的职责,以及如何识别出 ...
- 领域驱动设计(DDD)实践之路(一)
本文首发于 vivo互联网技术 微信公众号 链接: https://mp.weixin.qq.com/s/gk-Hb84Dt7JqBRVkMqM7Eg 作者:张文博 领域驱动设计(Domain Dr ...
- 我的“第一次”,就这样没了:DDD(领域驱动设计)理论结合实践
写在前面 插一句:本人超爱落网-<平凡的世界>这一期,分享给大家. 阅读目录: 关于DDD 前期分析 框架搭建 代码实现 开源-发布 后记 第一次听你,清风吹送,田野短笛:第一次看你,半弯 ...
- [.NET领域驱动设计实战系列]专题七:DDD实践案例:引入事件驱动与中间件机制来实现后台管理功能
一.引言 在当前的电子商务平台中,用户下完订单之后,然后店家会在后台看到客户下的订单,然后店家可以对客户的订单进行发货操作.此时客户会在自己的订单状态看到店家已经发货.从上面的业务逻辑可以看出,当用户 ...
随机推荐
- Django-路由层
URL配置(URLconf)就像Django所支撑网站的记录.它的本质是URL要为该URL调用的视图函数之间的映射表:你就是以这种方式告诉Django,对于客户端发来的某个URL调用哪一段逻辑代码对应 ...
- Tomcat安装7.0.91
版本升级,JDK 1.7,Tomcat从7.0.73升级到7.0.91 为什么升级?解决安全漏洞! 升级就正常流程,下载*.tar.gz ,解压,改配置. 但碰到神奇的坑: 1.server.xml中 ...
- Git学习笔记05-撤销修改
使用 git checkout -- file可以撤销工作区的修改 一种是修改后还没有放到暂存区,撤销修改回到和版本库一模一样的状态 lesson.txt文件已经提交到版本库了,内容如图. 修改一 ...
- DUILIB入门简明教程
电子书下载: DUILIB入门简明教程.chm 文章作者: Alberl 电子书制作: 邓学彬 目录: 2013 duilib入门简明教程 -- 前言(1) 2013 duilib入门简明教程 ...
- 题解-洛谷4921&4931 情侣?给我烧了!(加不加强无所谓版)
Problem 简单版 & 加强版 题目概要(其实题面写得很清楚,这里搬运一下): \(n\) 对情侣排座位,恰有 \(n\) 排座位,每排 \(2\) 个座位,在一个就座方案中所有人会将将座 ...
- 004_wireshark专题
一.常用的wireshark搜索语法 (1) http.request.uri contains "admin/activities" #搜索URL包含"admin/ac ...
- $Django 数据库图片渲染设计 站点设计 截断函数
1.数据库图片渲染设计 1.模型层 class User_info (AbstractUser): head_img = models.FileField (upload_to='test', def ...
- mysql alter 效率
2017年9月15日 10:36:54 星期五 今天遇到一个效率问题记下来: 场景: mysql要更改一下表字段的注释, 因为sql语句问题, 导致更新了整张表.. 错误: ) UNSIGNED ' ...
- Laravel 5.2--改变数据库字段值,编辑时候,默认选中
模型 <?php namespace App\Models; use App\Helpers\ImageHelper; use App\Libraries\Nestedset\NodeTrait ...
- word在线预览编辑器
https://blog.csdn.net/liuli283/article/details/73776685 https://blog.csdn.net/mxljiayou/article/deta ...