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实践案例:引入事件驱动与中间件机制来实现后台管理功能
一.引言 在当前的电子商务平台中,用户下完订单之后,然后店家会在后台看到客户下的订单,然后店家可以对客户的订单进行发货操作.此时客户会在自己的订单状态看到店家已经发货.从上面的业务逻辑可以看出,当用户 ...
随机推荐
- SpringCloud Netflix Ribbon(负载均衡)
⒈Ribbon是什么? Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡工具. Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负 ...
- Python open() 函数
open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=T) 模式 描述 r 以只读方 ...
- Spring Boot默认Initializer(1)——ConfigurationWarningsApplicationContextInitializer
ConfigurationWarningsApplicationContextInitializer的作用是用来报告Spring容器的一些常见的错误配置的.这个类中定义了两个内部类: 1. 定义了一个 ...
- ping 丢包或不通时链路测试说明【转】
转自:https://help.aliyun.com/knowledge_detail/40573.html?spm=5176.2020520165.121.d157.4fe170291Qdp4l#W ...
- Datasnap 服务端 (Server)Session 管理 --- 解决 全示例慢(Google)
Datasnap 服务端 (Server)Session 管理: http://docwiki.embarcadero.com/RADStudio/Tokyo/en/Server_Side_Ses ...
- 基于TCP(面向连接)的Socket编程
基于TCP(面向连接)的Socket编程 一.客户端: 1.打开一个套接字(Socket); 2.发起连接请求(connect); 3.如果连接成功,则进行数据交换(read.write.send.r ...
- ionic之AngularJS——手势事件
长按 : on-hold 在屏幕同一位置按住超过500ms,将触发on-hold事件: 你可以在任何元素上使用这个指令挂接监听函数: <any on-hold=“…”>…</any& ...
- 生产环境elasticsearch5.0报错IllegalArgumentException: number of documents in the index cannot exceed 2147483519的处理
最近几天的push:user:req数据写不到集群,报错如下: [--13T09::,][DEBUG][o.e.a.b.TransportShardBulkAction] [yunva_etl_es8 ...
- python安装提示No module named setuptools,wget提示ERROR 403: SSL is required
在下载安装一个python工具时提示报错No module named setuptools [root@kermit supervisor-3.3.0]$ sudo python setup.py ...
- 将list集合转json
public static class DataHelper { /// /// js 序列化器 /// static JavaScriptSerializer jss = new JavaScrip ...