代码之家  ›  专栏  ›  技术社区  ›  Steve

域驱动设计(LINQ到SQL)-如何删除聚合的部分?

  •  7
  • Steve  · 技术社区  · 17 年前

    我似乎让自己对整个ddd-linqtosql业务有点困惑。我正在使用pocos和linq to sql构建一个系统,我有用于聚合根的存储库。 因此,例如,如果您有order类->orderline,则您有order的存储库,但没有orderline,因为order是聚合的根。存储库具有用于删除订单的delete方法,但如何删除订单行? 您可能以为在order上有一个名为removeOrderLine的方法,该方法从OrderLines集合中删除了该行,但它还需要从基础的L2S表中删除该OrderLine。由于没有Orderline的存储库,您应该怎么做?

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

    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!
        }
    }
    

    我很想知道其他人都做了些什么,因为我不是唯一一个在这件事上挣扎的人。我希望…谢谢

    4 回复  |  直到 16 年前
        1
  •  2
  •   eglasius    17 年前

    在调用相关逻辑的顺序上调用removeOrderLine。这不包括对它的持久版本进行更改。

    稍后,在存储库上调用一个save/update方法,该方法接收修改后的订单。具体的挑战在于知道域对象中发生了什么变化,其中有几个选项(我确信比我列出的选项还多):

    • 让域对象跟踪更改,这将包括跟踪X需要从订单行中删除。类似于实体跟踪的内容也可能被排除在外。
    • 加载持久化版本。在存储库中有识别持久版本和内存版本之间差异的代码,并运行更改。
    • 加载持久化版本。在根聚合中包含代码,这将获得给定原始根聚合的差异。
        2
  •  1
  •   eduncan911    17 年前

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

    也就是说,聚合根控件上的方法可以访问其相关对象,而不是本身。另外,不要在聚合根上公开复杂类型(即在示例中声明的lines()IList集合)。这违反了decremeter(sp ck)定律,即不能“点走”到order.lines.add()等方法。

    此外,还违反了允许客户端访问对聚合根上的内部对象的引用的规则。聚合根 可以 返回内部对象的引用。只要外部客户端不允许保存对该对象的引用。也就是说,你的“命令行”进入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()的实例并对其执行操作,因为sales()或order()聚合根以及它们的存储库或工厂都不拥有对sales的控制权order()实体。

    我强烈推荐 this free book 由abel avram和floyd marinescu在域驱动器设计(ddd)上直接回答您的问题,在shrot 100页的大字。以及如何将域实体更多地分离为模块等。

    编辑:添加了更多代码

        3
  •  1
  •   B Nelson    16 年前

    在解决了这个问题之后,我找到了解决办法。在查看了设计器使用l2sl生成的内容之后,我意识到解决方案是order和orderline之间的双向关联。一个订单有多个订单行,而一个订单行只有一个订单。解决方案是使用双向关联和一个名为deleteonnull的映射属性(您可以用google搜索完整的信息)。最后一件我遗漏的事情是,您的实体类需要注册从l2s实体集中添加和删除事件。在这些处理程序中,必须将订单行上的订单关联设置为空。如果您查看L2S设计器生成的一些代码,可以看到一个这样的示例。

    我知道这是一个令人沮丧的问题,但经过几天的努力,我已经让它发挥作用。

        4
  •  0
  •   Steve    17 年前

    作为后续行动…… 我已经切换到使用nhibernate(而不是链接到sql),但实际上您不需要订单行的repo。如果您只是按顺序从集合中删除医嘱行,那么它将只从数据库中删除医嘱行(假设您已正确完成映射)。 当我与内存存储库交换时,如果您想搜索特定的订单行(不知道订单父项),可以编写一个linq to nhibernate查询,将订单链接到orderlineid=值的订单行。这样,当从数据库和内存中查询时,它就会工作。好吧,给你……