我一直在学习 CQRS / ES 。看一下小例子项目,我经常看到事件改变了实体状态。例如,如果我们查看Order
聚合根:
public class Order : AggregateRoot {
private void Apply(OrderLineAddedEvent @event) {
var existingLine = this.OrderLines.FirstOrDefault(
i => i.ProductId == @event.ProductId);
if(existingLine != null) {
existingLine.AddToQuantity(@event.Quantity);
return;
}
this.OrderLines.Add(new OrderLine(@event.ProductId, @event.ProductTitle, @event.PricePerUnit, @event.Quantity));
}
public ICollection<OrderLine> OrderLines { get; private set; }
public void AddOrderLine(/*parameters*/) {
this.Apply(new OrderLineAddedEvent(/*parameters*/));
}
public Order() {
this.OrderLines = new List<OrderLine>();
}
public Order(IEnumerable<IEvent> history) {
foreach(IEvent @event in history) {
this.ApplyChange(@event, false);
}
}
}
public abstract class AggregateRoot {
public Queue<IEvent> UncommittedEvents { get; protected set; }
protected abstract void Apply(IEvent @event);
public void CommitEvents() {
this.UncommittedEvents.Clear();
}
protected void ApplyChange(IEvent @event, Boolean isNew) {
Apply(@event);
if(isNew) this.UncommittedEvents.Enqueue(@event);
}
}
当应用OrderLineAddedEvent
时,它会通过添加新的订单行来改变Order
。但我不明白这些事情:
Order
的相应处理程序?我如何在技术上实现这一点?我应该使用服务总线传输事件吗?答案 0 :(得分:5)
我还在尝试ES,所以这仍然是一种意见而不是任何指导:)
在某个阶段,Jan Kronquist发现了这篇文章:http://www.jayway.com/2013/06/20/dont-publish-domain-events-return-them/
它的要点是应该从域返回事件而不是从域内调度。这真的引起了我的共鸣。
如果要采用更加传统的方法,而使用正常的面向持久性的存储库,应用层将处理事务和存储库访问。只需调用域即可执行该行为。
此外,域名应始终坚持持久性无知。让聚合根维护一个事件列表对我来说似乎总是有点奇怪,我绝对不喜欢让我的AR继承一些共同的基础。它感觉不够干净。
所以用你所拥有的东西把它放在一起:
public OrderLineAddedEvent AddOrderLine(/*parameters*/) {
return this.Apply(new OrderLineAddedEvent(/*parameters*/));
}
在我的POC中,我还没有使用IEvent
标记界面,而只使用object
。
现在应用层重新控制了持久性。
我有一个实验性的GitHub存储库:
我暂时没有时间看它,我知道我已经做了一些改动,但欢迎你看一下。
基本的想法是,应用层将使用EventStore
/ EventStream
来管理聚合的事件,其方式与应用程序相同图层将使用Repository
。 EventStream
将应用于汇总。从域行为返回的所有事件都将添加到EventStream
,然后再次保留。
这使得所有面向持久性的位都不在域之外。
答案 1 :(得分:3)
实体不会神奇地更新自己。 Something (通常是服务)将调用实体的更新行为。因此,服务使用将生成和应用事件的实体,然后服务将通过存储库保存实体,然后它将从实体获取新事件并发布它们。
另一种方法是事件存储本身发布事件。
事件采购是指将实体状态表示为事件流,这就是实体通过生成和应用事件来更新自身的原因,它需要创建/添加对事件流的更改。该流也是它存储在db(即事件存储库)中的内容。
答案 2 :(得分:1)
最近我将我的实体分成两个对象。
首先是我称之为Document对象。这主要是一个仅限状态的ORM类,其所有配置都与信息的持久性有关。
然后我用Entity对象包装该Document,这实际上是一个包含所有行为的变异服务。
我的实体基本上是无状态的对象,当然除了包含的文档,但无论如何,我主要避免任何暴露于外部世界。