关于ABP聚合根类AggregateRoot的思考

AggregateRoot和Entity的区别

AggregateRoot继承于Entity,并实现了IGeneratesDomainEvents接口

public class AggregateRoot : Entity, IAggregateRoot, IEntity, IGeneratesDomainEvents
{
public AggregateRoot();

[NotMapped]
public virtual ICollection DomainEvents { get; }
}

在DDD里面聚合根是一定对应一个实体

为什么要使用AggregateRoot

实现了IGeneratesDomainEvents,属性DomainEvents可以方便产生领域事件,这些事件在当前的工作单元完成之前自动的触发。

在ABP里面不会强迫使用聚合,但既然选用了ABP这个框架,那就是希望能使用DDD的开发模式,所以使用AggregateRoot是更好的实践。

IGeneratesDomainEvents解析

先贴关键源码

public abstract class AbpDbContext : DbContext, ITransientDependency
{
public override int SaveChanges()
{
//从DomainEvents中拿到定义的领域事件
var changeReport = ApplyAbpConcepts();
var result = base.SaveChanges();
//数据保存后,触发领域事件
EntityChangeEventHelper.TriggerEvents(changeReport);
return result;
}

protected virtual EntityChangeReport ApplyAbpConcepts()
{
var changeReport = new EntityChangeReport();
...
var entries = ChangeTracker.Entries().ToList();
foreach (var entry in entries)
{
...
AddDomainEvents(changeReport.DomainEvents, entry.Entity);
}

return changeReport;
}
protected virtual void AddDomainEvents(List domainEvents, object entityAsObj)
{
var generatesDomainEventsEntity = entityAsObj as IGeneratesDomainEvents;
if (generatesDomainEventsEntity == null)
{
return;
}

if (generatesDomainEventsEntity.DomainEvents.IsNullOrEmpty())
{
return;
}

domainEvents.AddRange(generatesDomainEventsEntity.DomainEvents.Select(eventData => new DomainEventEntry(entityAsObj, eventData)));

//清空DomainEvents
generatesDomainEventsEntity.DomainEvents.Clear();
}
}

怎么在聚合根里面如何发布领域事件

DomainEvents.Add(new BlogUrlChangedEventData(this, oldUrl));

没有调用EventBus.Trigger来触发领域事件,那添加到DomainEvents的EventData是在什么时候触发的呢?

阅读下源码不难发现,重写了SaveChanges方法,在序列化到数据库后触发领域事件

聚合根里面有两个甚至多个方法都调用DomainEvents来产生领域事件,会不会造成事件多次触发呢?

不会,阅读源码,很容易发现DomainEvents只是负责临时传递EventData,传递给了changeReport这个局部变量后,就会清空,实际上真正触发领域事件的是changeReport。

简单说,就是每次调用SaveChanes后就会清空DomainEvents

posted @ 2017-01-11 11:07  莫笑少年痴狂  阅读(6969)  评论(1)    收藏  举报