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实践案例:引入事件驱动与中间件机制来实现后台管理功能
一.引言 在当前的电子商务平台中,用户下完订单之后,然后店家会在后台看到客户下的订单,然后店家可以对客户的订单进行发货操作.此时客户会在自己的订单状态看到店家已经发货.从上面的业务逻辑可以看出,当用户 ...
随机推荐
- 20165231 2017-2018-2 《Java程序设计》第1周学习总结
本周学习的是一些java简单的基本编译,反编译和解释器.然后学习使用git了上传到git@osc进行代码托管,git是初学的,需要建立库然后远程上传代码,如果建立失败或者因为种种缘故无法上传的可以一个 ...
- VS2017打包C#桌面应用
原文地址:https://blog.csdn.net/houheshuai/article/details/78518097 在要打包项目的解决方案 右键→添加→ 新建项目 后出现如下选择 如果没有V ...
- bigfile tablespace
背景 这次终于有个linux实际迁移oracle的机会了,之前都是学习实验.想起最早时,都是windows搞oracle,又让我想起多年期一个项目,数据量太大及计算逻辑太复杂,我用存储过程 ...
- C#winform 窗体缩放自适应
1. 首先在窗体上放上一个Panel容器,并将容器的Dock属性设为Fill,即所有的控件都放在了这个容器里. using System; using System.Collections.Gene ...
- 使用GeoServer导出地图数据GeoJSON并应用
在项目中,需要使用乡镇街道的地图边界,之前一直使用的是百度地图或Echarts地图,其没有这部分行政区的数据,需要在第三方购买数据,其提供的是shp文件 主文件:counties.shp 索引文件:c ...
- Light OJ 1058
题意: 简单的就组合数 C(m,n): 数据多,大, 要预处理: #include<bits/stdc++.h> using namespace std; typedef long lon ...
- 数据库之删除表数据drop、truncate和delete的用法
数据库中删除表数据的关键字,最常用的可能就是delete了,另外其实还有drop和truncate两个关键字. 老大:drop 命令格式:drop table tb ---tb表示数据表的名字,下 ...
- [PHP]php缓冲 output_buffering的使用详解
bufferbuffer是一个内存地址空间,Linux系统默认大小一般为4096(4kb),即一个内存页.主要用于存储速度不同步的设备或者优先级不同的设备之间传办理数据的区域.通过buffer,可以使 ...
- 4)django-视图view
视图是django功能函数,结合url使用 1.视图方式 视图方式经常用的有两种 用户GET获取数据 用户POST提交数据 用户第一次访问页面是GET 用户 ...
- python按照字典排序
d = {'a':1,'b':4,'c':2} 方法一: sorted(d.items(),key = lambda x:x[1],reverse = True) 方法二: import operat ...