领域驱动设计是以程序员、项目经理和企业主都能理解的方式对问题进行建模...http://martinfowler.com/bliki/UbiquitousLanguage.html
您会发现问题所有者不使用“从购物车中删除”一词,他们会使用“从购物车中删除商品”或“将商品添加到购物车”一词。
总变量的检测和计算由聚合根实体(购物车)管理,其中包含子实体(产品)。 http://dddcommunity.org/library/vernon_2011/
对于 DDD 项目,不要从担心如何存储数据开始解决问题,而是从担心代码(实体)如何建模正在解决的问题开始。
由于人们在解释问题时会根据事件来说话,“用户将商品添加到购物车,所以总数应该更新”,因此将问题建模为在事件发生时做出反应并应用更新的事物通常是有意义的.
这就是事件溯源真正大放异彩的地方,因为实体代码和存储之间不再存在不匹配(有时可能来自使用 ORM 工具),因为所有内容(问题和代码)都以事件的母语解释。
通过存储每个事件,您可以重播所有事件并逐步重建当前状态。
每个存储的事件都是不可变的,一旦创建就无法更改,这将通过构造函数中的参数设置只读字段来完成:
// in UserAddedItemToCart.cs
public class UserAddedItemToCart
{
public UserAddedItemToCart(int productId)
{
this.ProductId = productId;
}
public readonly int ProductId;
}
// in UserRemovedItemFromCart.cs
public class UserRemovedItemFromCart
{
public UserAddedItemToCart(int productId)
{
this.ProductId = productId;
}
public readonly int ProductId;
}
// in Cart.cs
public class Cart : AggregateBase
{
private readonly HashSet<int> Items = new HashSet<int>();
public void AddItem(int itemId)
{
RaiseEvent(new UserAddedItemToCart(itemId));
}
public void RemoveItem(int itemId)
{
RaiseEvent(new UserRemovedItemFromCart(itemId));
}
// wired up via base class
protected void Apply(UserAddedItemToCart evnt)
{
this.Items.Add(evnt.ProductId);
}
// wired up via base class
protected void Apply(UserRemovedItemFromCart evnt)
{
this.Items.Remove(evnt.ProductId);
}
public int[] ItemsInCart
{
get { return this.Items.ToArray(); }
}
}
当我遇到与您相同的问题时,我发现一些链接很有用,并且从删除事物的想法转变为考虑发生的事件:
https://geteventstore.com/
https://geteventstore.com/blog/20130220/getting-started-part-2-implementing-the-commondomain-repository-interface/
https://github.com/NEventStore/CommonDomain
您可以查看 AggregateBase 以查看它如何存储事件,以及上面的 implementation-the-commondomain-repository-interface 链接以查看这两个链接如何在没有公共设置器的情况下重建所有内容(您仍然应该为您的代码建模,以便一切都受到聚合根的保护)。
不要过于关注 ORM 如何存储您的数据。如果通过 CRUD 访问和控制创建、读取、更新、删除更有意义,那么只需以这种方式建模问题。
带有事件溯源的 DDD 可能会增加更多的复杂性,因此请负责任地使用,并且仅当它可以在减少时间为您的项目增加价值以便以后添加功能的情况下使用。