7

我似乎对整个 DDD\LinqToSql 业务感到有些困惑。我正在使用 POCOS 和 linq to sql 构建一个系统,并且我有聚合根的存储库。因此,例如,如果您有 Order->OrderLine 类,那么您有一个 Order 存储库,但没有 OrderLine,因为 Order 是聚合的根。仓库有删除Order的delete方法,但是如何删除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!
    }
}

我很想知道其他人做了什么,因为我不能成为唯一一个为此苦苦挣扎的人....我希望....谢谢

4

4 回答 4

2

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

稍后,您在存储库上调用 Save/Update 方法,该方法接收修改后的订单。具体的挑战在于了解域对象中发生了什么变化,有几个选项(我相信不止我列出的选项):

  • 让域对象跟踪更改,其中包括跟踪需要从订单行中删除 x。类似于实体跟踪的东西也可能被排除在外。
  • 加载持久版本。在存储库中拥有识别持久版本和内存版本之间差异的代码,并运行更改。
  • 加载持久版本。在根聚合中有代码,可以让您在给定原始根聚合的情况下获得差异。
于 2009-03-17T01:46:53.380 回答
1

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

话虽如此,您的 Aggregate Root 上的方法控制对其相关对象的访问 - 而不是自身。此外,切勿在聚合根(即您在示例中说明的 Lines() IList 集合)上公开复杂类型。这违反了减量法则 (sp ck),即您不能“点走”您的方法,例如 Order.Lines.Add()。

而且,您违反了允许客户端访问对聚合根上的内部对象的引用的规则。聚合根可以返回内部对象的引用。只要不允许外部客户端持有对该对象的引用。即,您传递给RemoveLine() 的“OrderLine”。您不能允许外部客户端控制模型的内部状态(即 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() 聚合根,也不是它们的存储库或工厂,拥有对 SalesOrder() 实体的控制权。

我强烈推荐这本由 Abel Avram 和 Floyd Marinescu 撰写的关于域驱动设计 (DDD) 的免费书籍,因为它以 100 页的大字体直接回答了您的问题。以及如何将您的域实体更多地解耦为模块等。

编辑:添加更多代码

于 2009-03-17T02:09:54.777 回答
1

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

我知道这是一个令人沮丧的问题,但经过几天的努力,我已经开始工作了。

于 2009-11-04T23:10:27.570 回答
0

作为后续....我已切换到使用 nhibernate(而不是链接到 sql),但实际上您不需要 OrderLine 的存储库。如果您只是从 Order 中的集合中删除 OrderLine,它只会从数据库中删除 OrderLine(假设您已正确完成映射)。当我用内存存储库换出时,如果您想搜索特定的订单行(不知道订单父级),您可以编写一个 linq to nhibernate 查询,将订单链接到 orderlineid = value 的 orderline。这样,它在从数据库和内存中查询时就可以工作。那么你去...

于 2009-04-14T21:39:07.993 回答