当前位置: 首页 > 知识库问答 >
问题:

实体真的只能属于一个聚合吗?

颜思淼
2023-03-14

我正在学习DDD,刚刚面临一个我无法解决的问题。假设我们有以下领域:

public class Hotel : AggregateRoot {
    public List<Room> Rooms { get; private set; }
}

public class Room : Entity {
    public string Name { get; set; }
    public int Number { get; set; }
}

现在我们想模拟客房预订。

public class RoomReservationRecord : Aggregate {
    public string CustomerName { get; set; }
    public Room Room { get; set; } // <- this is problem
    public DateTime DateFrom { get; set; }
    public DateTime DateTo { get; set; }
}

清晰可见,2个聚合包含(共享)单个实体。从业务角度来看是有意义的,但是从DDD角度来看,看起来2个聚合共享同一个实体。

  1. 这种方法是正确的,还是违反了“实体可以是单个聚合的一部分”规则

共有1个答案

南宫喜
2023-03-14

我会试着指出一些事情,但在一天结束时,这是一个建模练习,通常需要做出妥协。

不变量——在对象图中对对象进行分组的很大一部分(可能是主要原因)是确保执行某些规则。因此,如果没有房间可以被双重预订是一条商业规则,那么酒店可能是一个以房间和预订为实体的聚合根。

像这样的东西...但是这有一些缺点...

如。

class Hotel
{
    //members (eg. Rooms and Reservations) ...
    public Hotel(string name, ICollection<Room> rooms)
    {
        //...
    }

    bool TryMakeReservation(Reservation reservation)
    { 
        // if booked already return false
        return true;
    }
}

class Room
{
    //members ...
    public Room(int number, bool isBooked)
    {
        //..
    }
}

class Reservation  {
    public string CustomerName { get; private set; }
    public int RoomNumber { get; private set; }
    public DateTime DateFrom { get; private set; }
    public DateTime DateTo { get; private set; }

    Reservation(Parameters)
    {
        // ...
    }
}

注意:要使用此模型,您需要在每次预订时锁定酒店!

对于繁忙的酒店来说,这可能是不可接受的。有很多方法可以解决这个问题,比如在自动发布之前,先保留5分钟,除非确认了预订。或者存储活动列表,如果在同一时间段内,同一房间有两个预订,但没有结账活动,则启动一个流程,通知某人处理双人预订。

上下文-可能是房间列表和实际预订在不同的域中。想想从多个来源进行预订的频率,比如AirBnb,预订。com、酒店网站和/或亲自或通过电话到柜台。预订和可用房间的持久性在同一个域中可能没有意义。清洁时间表呢。一个房间在可用之前需要清洁,但这真的是在预订环境中处理的吗?

性能——如前所述,有时我们想要的模型是不可能的,因为从数据存储中获取大量查询数据的物理特性。用户、产品所有者等往往不在乎你的模型有多干净,如果它对性能影响太大。

存储库——从上面一点开始,因为聚合应该是一致的类型(即。其中的数据永远不应该处于不正确的状态),那么当您获取聚合时,它应该是一致的。如果Hotel有一个存储库但包含一个房间,并且Room是一个聚合根并有自己的存储库,并且存储库正在调用存储库,我会说您在扩展复杂性方面失败了。DDD的要点是提供一组模式和实践来帮助您处理复杂性。如果通过应用DDD原则,您将复杂性增加了一个步骤,而随着新功能的添加,未来复杂性的较小步骤永远不会将其资本化,那么DDD可能不是项目中当时使用的正确工具。

与Ids上的链接文章对话。不使用原语类型可以缓解这里的许多问题。专注于寻找有价值的对象可以让你更加清晰,真正帮助你表达自己的领域。即使你不使用DDD,这也是一个很有价值的实践,这就是为什么我写了一系列关于它们的文章,却从未提及价值对象。。。我想。

我希望这有帮助。DDD对我来说甚至比FP更重要,它有很多非常有价值的想法,可以帮助创建可扩展到需求的可维护代码。比这更好的是,它关注代码之外的软元素,比如协作,并共享一种带来更多价值的语言,但它们只是需要应用的指导原则,因为你想要某种收益(并且愿意支付任何产生的成本)。它们不是适用的规则,很少只是一条错误和正确的道路。

 类似资料:
  • 问题内容: 我想过滤出字段“ A”等于“ a”的文档,并且我想同时考虑字段“ A”,当然不包括先前的过滤器。我知道您可以将过滤器“置于查询之外”,以便在不应用该过滤器的情况下获得构面,例如: elasticsearch 单反 这非常好,但是如果我有多个滤镜和构面,每个滤镜和构面应该互相排斥,会发生什么?例: 也就是说,对于方面AI,希望保留除A:a以外的所有过滤器,对于方面B希望保留除B:b以外的

  • 我想过滤掉字段'a'等于'a'的文档,同时我想对字段'a'进行刻面处理,当然不包括前面的过滤器。我知道您可以将筛选器放在查询的“外部”,以便在不应用该筛选器的情况下获得方面,例如: 弹性搜索 索尔尔 也就是说,对于方面A,我希望保留除A:A以外的所有过滤器,对于方面B,我希望保留除B:B以外的所有过滤器,以此类推。最明显的方法是执行n个查询(n个方面中的每一个),但我不想这样做。

  • 考虑一个域,其中客户、公司、雇员等拥有一个ContactInfo属性,该属性又包含一组地址、电话、电子邮件等。 以下是我的缩写联系人信息: 一些注意事项(我不是100%确定这些是否是EF的“最佳实践”): 地址集合是虚拟的,以允许延迟加载 集合上的私有设置器禁止集合替换 集合是一个以确保每个联系人没有重复的地址 使用方法,我可以确保始终且最多有一个主地址.... 我希望(如果可能)阻止通过方法添加

  • 我在后端使用Node和MySQL,在前端使用Polymer,但我无法让Polymer呈现JSON数组。 节点/MySQL代码 聚合物前端 在另一个测试场景中(正在工作),我手动将JSON数组输出复制到一个文件中,将参数从更改为,并重新加载electron应用程序;并且Polymer能够在下拉列表中显示JSON内容。

  • 问题内容: 在我的一项工作中,我有以下代码: 这始终无法删除具有以下错误的实体: DELETE语句与REFERENCE约束“ FK966F0D9A66DB1E54”冲突。数据库“ TFADB”的表“ dbo.MonthlyReport_categories”的列“ MonthlyReport_id”中发生了冲突。 我如何指定映射,以便在删除报告时删除category集合中的元素? 问题答案: 级联