删除域驱动设计中的实体

时间:2015-02-20 21:26:30

标签: c# entity-framework domain-driven-design

我最近开始探索域驱动设计的概念。我发现的大多数示例和解释都假设有一种ORM工具用于保存存储库中的实体。

目前,我倾向于使用Entity Framework作为ORM工具,但是我想到了一些可能适用于其他工具的问题。例如,假设存在具有多个OrderLine实体的根聚合Order作为子:

  • Order具有公开OrderLines
  • 的只读集合属性
  • 订单行的添加和删除是通过订单
  • 上的特定方法实现的
  • 添加或删除订单时,需要相应更改订单的总价

    1. 保存订单时检测新OrderLines添加的首选方法是什么?在存储库中,我可以检查id的值,以检测是否需要添加或更新行。这是正确的方法吗?

    2. 订单存储库如何检测行的删除?

    3. 当某些属性只能是私有设置时,如何从数据库中恢复实体?

在根聚合上公开公共可修改集合时,这些示例很容易实现,但这样就可以在不使用专用方法的情况下修改集合。私有财产的修复也是如此:让它们公开可设置。

如果我不想使用ORM工具,但是直接进行SQL查询,那么在这种情况下如何最好地解决问题呢?理想情况下,域模型在这种情况下不需要修改,因为更改的持久方式是存储库的实现细节,因此可以是EF,NHibernate或普通的旧SQL。

2 个答案:

答案 0 :(得分:1)

领域驱动设计是以编码员和项目经理以及企业主都能理解的方式对问题进行建模...... http://martinfowler.com/bliki/UbiquitousLanguage.html

你会发现问题所有者不使用购物车中的DELETE这个词,他们会使用购物车中的REMOVE项或ADD项来购物。

总变量的检测和计算由具有子实体(产品)的聚合根实体(购物车)管理。 http://dddcommunity.org/library/vernon_2011/

对于DDD项目,不要担心如何存储数据而开始解决问题,首先要担心代码(实体)如何模拟正在解决的问题。

由于人们在解释问题时会根据事件说话,并且#34;用户将项目添加到购物车中,因此总计应该更新",将问题建模为反应的事情通常是有意义的在事件发生时应用更新。

这是事件采购真正发光的地方,因为实体代码和存储之间不再存在不匹配,有时可能来自使用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链接,看看这两个链接如何在没有公共setter的情况下重建所有内容(您仍然应该为代码建模一切都受到聚合根的保护。)

不要过于关注ORM如何存储您的数据。如果通过CRUD访问更有意义并将控制作为创建,读取,更新,删除进行访问,那么只需对问题进行建模。

带有事件源的DDD可能会增加更多的复杂性,因此请负责任地使用,并且只有在以后的时间内为您的项目增加价值才能增加功能。

答案 1 :(得分:0)

我建议将事件采购视为一种持久化数据的方式。

  

事件采购是一种通过存储确定应用程序当前状态的历史记录来保持应用程序状态的方法。

与DDD结合使用效果非常好。

https://msdn.microsoft.com/en-us/library/jj591559.aspx#sec1

在您的场景中,您描述的是存储类似OrderLineItemAddedEvent或OrderLineItemDeletedEvent(每个都包含通常存储为JSON blob的所有项信息),然后当您想要重新加载聚合时,您只需重放所有事件。命令他们被坚持将你的对象恢复到正确的状态。

相关问题