你如何删除一个聚合的部分?

我似乎已经让自己陷入了这整个DDD LinqToSql业务的混乱之中。 我正在建立一个系统使用POCOS和LINQ到SQL,我有聚合根的仓库。 因此,例如,如果您有Order-> OrderLine类,则您有Order的存储库,但不包含OrderLine,因为Order是聚合的根。 存储库具有删除订单的删除方法,但是如何删除OrderLines? 你会想到你在Order上有一个名为RemoveOrderLine的方法,它从OrderLines集合中删除了这一行,但它也需要从底层的l2s表中删除OrderLine。 由于没有OrderLine的存储库,你应该怎么做?

也许有专门的公共仓库来查询域对象实际用于删除聚合内的东西的根和内部通用存储库?

public class OrderRepository : Repository<Order> {
    public Order GetOrderByWhatever();
}

public class Order {
    public List<OrderLines> Lines {get; set;} //Will return a readonly list
    public RemoveLine(OrderLine line) {
        Lines.Remove(line);
        //************* NOW WHAT? *************//
        //(new Repository<OrderLine>(uow)).Delete(line) Perhaps??
        // But now we have to pass in the UOW and object is not persistent ignorant. AAGH!
    }
}

我很想知道其他人做了什么,因为我不能成为唯一一个正在努力的人......我希望......谢谢


您可以调用订单上的RemoveOrderLine调用相关逻辑。 这不包括对其持久版本进行更改。

稍后在存储库上调用保存/更新方法,该方法会接收修改后的订单。 具体的挑战在于了解领域对象中有哪些变化,有几个选项(我确信这些选项不止我列出的):

  • 让域对象跟踪更改,其中包括跟踪需要从订单行删除x的跟踪。 类似于实体追踪的东西也可能被分解出来。
  • 加载持久版本。 在存储库中安装代码,以识别持久版本和内存版本之间的差异,然后运行更改。
  • 加载持久版本。 在根集合中有代码,可以让您获得原始根集合的差异。

  • 首先,您应该公开接口以获取对您的聚合根(即Order())的引用。 使用工厂模式来新建聚合根的新实例(即Order())。

    有了这个说法,您的Aggregate Root控件上的方法就可以访问其相关对象 - 而不是它本身。 此外,不要在集合根上公开复杂类型(例如,您在示例中指出的Lines()IList集合)。 这违反了递减法(sp ck)的规律,即你不能“点漫游”你的方式,如Order.Lines.Add()。

    而且,您违反了允许客户端访问对聚合根目录上的内部对象的引用的规则。 聚合根可以返回内部对象的引用。 只要不允许外部客户端持有对该对象的引用即可。 即,您将“OrderLine”传递给RemoveLine()。 您不能允许外部客户端控制模型的内部状态(即Order()及其OrderLines())。 因此,您应该期望OrderLine成为相应采取行动的新实例。

    public interface IOrderRepository
    {
      Order GetOrderByWhatever();
    }
    
    internal interface IOrderLineRepository
    {
      OrderLines GetOrderLines();
      void RemoveOrderLine(OrderLine line);
    }
    
    public class Order
    {
      private IOrderRepository orderRepository;
      private IOrderLineRepository orderLineRepository;
      internal Order()
      {
        // constructors should be not be exposed in your model.
        // Use the Factory method to construct your complex Aggregate
        // Roots.  And/or use a container factory, like Castle Windsor
        orderRepository = 
                ComponentFactory.GetInstanceOf<IOrderRepository>();
        orderLineRepository = 
                ComponentFactory.GetInstanceOf<IOrderLineRepository>();
      }
      // you are allowed to expose this Lines property within your domain.
      internal IList<OrderLines> Lines { get; set; }  
      public RemoveOrderLine(OrderLine line)
      {
        if (this.Lines.Exists(line))
        {
          orderLineRepository.RemoveOrderLine(line);
        }
      }
    }
    

    不要忘记你的工厂创建Order()的新实例:

    public class OrderFactory
    {
      public Order CreateComponent(Type type)
      {
        // Create your new Order.Lines() here, if need be.
        // Then, create an instance of your Order() type.
      }
    }
    

    您的外部客户端有权通过接口直接访问IOrderLinesRepository,以获取聚合根目录中值对象的引用。 但是,我试图通过将我的引用全部从集合根的方法中强制阻止。 所以,你可以把上面的IOrderLineRepository标记为内部的,这样它就不会被暴露。

    实际上,我将所有聚合根创建组合成多个工厂。 我不喜欢这样的做法:“有些聚合根将有复杂类型的工厂,有些则不会”。 在整个领域建模中遵循相同的逻辑要容易得多。 “哦,所以Sales()是一个像Order()这样的聚合根,它也必须有一个工厂。”

    最后一点需要注意的是,如果有一个使用SalesOrder()的组合,即使用Sales()和Order()两种模型,则您将使用服务来创建SalesOrder()或Order()聚合根,或其存储库或工厂,都拥有对SalesOrder()实体的控制权。

    我非常高度推荐Abel Avram和Floyd Marinescu在Domain Drive Design(DDD)这本免费的书,因为它直接回答你的问题,在一个100页的大版画里。 以及如何更多地将您的域实体解耦为模块等。

    编辑:添加更多的代码


    在解决这个问题之后,我找到了解决方案。 在看了设计师用l2sl产生的东西后,我意识到解决方案是在订单和订单之间的双向关联中。 订单有很多订单,订单行有一个订单。 解决方案是使用双向关联和称为DeleteOnNull的映射属性(您可以通过谷歌获得完整的信息)。 我错过的最后一件事是你的实体类需要注册l2s实体集中的添加和删除事件。 在这些处理程序中,您必须将订单行上的订单关联设置为空。 如果你看一下l2s设计者生成的一些代码,你可以看到这个例子。

    我知道这是一件令人沮丧的事情,但经过与之奋斗的日子之后,我已经开始工作了。

    链接地址: http://www.djcxy.com/p/42369.html

    上一篇: How do you delete parts of an aggregate?

    下一篇: Secure and efficient way to modify multiple files on POSIX systems?