Domain driven design 领域驱动设计:如何设计具有依赖关系的关系聚合

Domain driven design 领域驱动设计:如何设计具有依赖关系的关系聚合,domain-driven-design,aggregate,Domain Driven Design,Aggregate,我的领域是关于程序管理。我有一个程序(聚合根目录),它必须有一个客户(聚合根目录)。因此,在创建新程序时,我需要一个CustomerID,因为我已经阅读了聚合,它应该只通过引用保存对其他聚合的引用 以下是我的业务规则: 随着时间的推移,客户会变得活跃和不活跃 如果某个客户由于某种原因被禁用,则与该客户相关的所有程序也应被禁用 如果客户处于非活动状态,则无法激活程序 我已经执行了规则1和规则2。让我难堪的是#3 我可以想出3种解决方案: 程序保存对客户聚合的引用 引入一个域服务,用于检查客户是否处

我的领域是关于程序管理。我有一个程序(聚合根目录),它必须有一个客户(聚合根目录)。因此,在创建新程序时,我需要一个CustomerID,因为我已经阅读了聚合,它应该只通过引用保存对其他聚合的引用

以下是我的业务规则:

  • 随着时间的推移,客户会变得活跃和不活跃

  • 如果某个客户由于某种原因被禁用,则与该客户相关的所有程序也应被禁用

  • 如果客户处于非活动状态,则无法激活程序

  • 我已经执行了规则1和规则2。让我难堪的是#3

    我可以想出3种解决方案:

  • 程序保存对客户聚合的引用

  • 引入一个域服务,用于检查客户是否处于活动状态,并将其传递给Program.Activate(CustomerActivityCheckService服务)

  • 让应用程序服务查找客户并将其传递给Program.Activate(客户)

  • 哪一个是最好的解决方案

    更新

    我看到@ConstaningalBrus和@plalx的两种观点,我想提出一个折衷方案。我可以创建一个
    CustomerStatusChecker
    服务吗?该方法将具有以下签名:
    CustomerStatus CheckStatus(CustomerID id)
    然后我可以通过
    程序
    这样的服务:`Program.Activate(CustomerStatusChecker服务)


    此设计是否存在任何问题?

    解决方案1:它打破了不保留对其他聚合实例的引用的规则。该规则确保在一个事务中只修改一个聚合。如果您需要在单个事务中修改多个聚合,那么您的设计肯定是错误的

    解决方案2:我真的不喜欢在聚合中注入服务。我的聚合是纯函数,不涉及外部世界(I/O、存储库等)

    解决方案3:在某种程度上等同于1,即使它是一个临时引用(
    程序
    可以调用
    客户
    上的命令方法,从而在与
    程序
    相同的事务边界中修改
    客户

    我的解决方案是:在调用
    Program.activate()
    或将
    customerStatus
    传递给
    Program.activate()
    之前,在应用程序服务内部进行检查,然后让
    Program
    聚合决定它是否引发异常或发出事件

    更新:

    其思想是,您应该只将只读/可修改的数据传递给
    程序
    AR,以确保它不会修改其事务边界中的其他AR。此外,我们不应该让
    程序依赖于它不需要的东西,比如整个
    客户
    AR


    此外,如果架构是事件驱动的,那么通过聆听
    客户发出的正确事件,您可以保持
    程序
    AR同步:如果尚未激活,则使其“不可激活”;如果已激活,则将其停用,通过示例使用
    Saga

    解决方案1:它打破了不保留对其他聚合实例的引用的规则。该规则确保在一个事务中只修改一个聚合。如果您需要在单个事务中修改多个聚合,那么您的设计肯定是错误的

    解决方案2:我真的不喜欢在聚合中注入服务。我的聚合是纯函数,不涉及外部世界(I/O、存储库等)

    解决方案3:在某种程度上等同于1,即使它是一个临时引用(
    程序
    可以调用
    客户
    上的命令方法,从而在与
    程序
    相同的事务边界中修改
    客户

    我的解决方案是:在调用
    Program.activate()
    或将
    customerStatus
    传递给
    Program.activate()
    之前,在应用程序服务内部进行检查,然后让
    Program
    聚合决定它是否引发异常或发出事件

    更新:

    其思想是,您应该只将只读/可修改的数据传递给
    程序
    AR,以确保它不会修改其事务边界中的其他AR。此外,我们不应该让
    程序依赖于它不需要的东西,比如整个
    客户
    AR

    此外,如果架构是事件驱动的,那么通过聆听
    客户发出的正确事件,您可以保持
    程序
    AR同步:如果尚未激活,则使其“不可激活”;如果已激活,则使用示例a
    Saga
    ,将其停用

    哪一个是最好的解决方案

    没有最好的解决方案;这是一种权衡

    但与需求2和3一致的一个可能的解决方案是,您现有的模型是错误的——程序实体不是孤立的聚合,而是客户实体的一部分,因此应该由相同的聚合根控制

    提示可能是这样的:程序的生命周期适合于客户的生命周期;程序通常不会从一个客户迁移到另一个客户,每个客户的活动程序数量是有限制的

    另一种可能性是要求是“错误的”。探索这一点的一种方法是查看活动/非活动是由模型做出的决策,还是在其他地方做出并报告给模型的决策。另一个是检查违反此“规则”的业务成本

    如果模型没有立即发现客户,或者这是一个成本低廉的问题,那么您可能有一些空间来检测冲突并将其报告给人,而不是试图让模型完成所有的工作