DDD组合多个有界上下文

4
我希望能够请教您关于有界上下文集成的建议。我有一个使用案例让我陷入了困境:
  • 我有一个有界上下文用于合同管理。我可以向合同添加各种各样的外部组织作为参与方(例如)。对于每个参与方,选择他们的投资/贡献(例如总数的10%)。因此,合同管理是双重的:一方面是行政管理(添加参与方,管理多个日期等),另一方面是财务管理(计划跨越多年的贡献,检查贡献消耗等)。
  • 我还有另一个有界上下文用于预算。这个上下文负责组织层面的费用管理。例如:服务A将拥有1000欧元的费用容量。我们可以计划预算,之后每个组织方都可以消费,购买东西,他们的那部分。为了构建预算,负责企业预算的用户可以直接分配资金或集成年度合同财务组件。当我们在预算中集成合同部分时,我们会冻结预算内的数据,即将货币数据从一个数据库表复制到另一个数据库表中(添加一些审计信息)。我们只有一个数据库。

正是最后一部分让我苦恼。每个有界上下文都是一个专用的应用程序。在预算应用程序中,在当前预算中集成合同部分之后,我需要显示预算详细信息行。不幸的是,在预算表中,我只有货币数据,而没有关于合同的基本信息(对象、参考等)。

我的想法是:

  • 有时在有界上下文之间复制数据并不是坏事。我冻结了合同的货币部分。我还可以冻结/复制合同的对象和参考。然后查询将仅在预算上下文内进行。但是问题在于数据复制。今天我需要对象/参考,如果明天我需要更多字段……我将需要领域事件管理来保持合同/预算之间的数据同步。
  • 查询预算,并为每个行查询contract服务,该服务将返回所需的数据。这使得每个上下文都是自治的,但是我需要进行大量数据库请求来丰富预算详细信息行对象。
  • 只需在数据库级别进行一次连接,我们就可以使其正常工作。这里的耦合如何?这是简单的解决方案,也是我们今天所做的(这是共享内核吗?)。似乎我们无法承受在不重建预算应用程序的情况下更改合同结构。我没有上下文之间的编程合同。

我的问题是:

我如何构建此UI屏幕,需要从预算上下文获取数据,并且每个详细信息行都需要从合同上下文获取数据?

附注:

  • 也许从一开始就错了上下文识别和范围(它是一个旧的设计)。
  • 我希望将上下文保持分开(松耦合)。如果我们可以在上下文之间指定设计契约,维护会更容易(或者不是吗?)。
  • 我没有看到如何集成这些上下文(我需要重新阅读共享内核、上游/下游等)。

这是一个额外的、独立的有界上下文。它与现有的有界上下文有一些重叠,这很容易让你走上错误的道路(合并上下文或将额外的行为放在不属于它的上下文中)。 - MattDavey
我可以问一下你打算如何对这个新的第三方上下文进行建模吗?(将所有领域逻辑放在其中并查询它?)这个新的上下文也将以“预算”为导向。这增加了复杂性。我理解你的评论。我会考虑一下,看看这个上下文如何出现。在实施DDD时,Vernon谈到了它,但没有提供示例:/我感觉很无助! - Archange
我将我的评论扩展成了一个答案。 - MattDavey
1个回答

1
这是一个额外的、独立的有界上下文。它与现有的有界上下文有一些重叠,这很容易让你走上错误的道路(合并上下文或将附加行为放在不属于该上下文的地方)。有时,在不同的有界上下文中拥有引用相同逻辑实体的实体是可以接受的,但它们只是为特定场景(例如在特定上下文中)提供了该实体的不同视图。电子商务场景是一个很好的例子。在大多数电子商务应用程序中,您将拥有"订单"的概念,但没有全局、明确的"订单"概念。在财务上下文中,订单仅仅是发票。在履行上下文中,订单仅仅是包装清单和发送货物的地址。在营销上下文中,订单代表了客户感兴趣的一小部分智能,可以用于未来的定向营销。所有这些实体都有一个共性,但您可能会看到至少3个单独的订单类,每个类都捕获了一个上下文内的订单概念。
在您的情况下,您有一个合同的有界上下文和一个预算的有界上下文。对我来说,您现在有了另一种观察这些实体的方式,特别是它们如何相互作用。这是实体的新视图,可以在其自己的上下文中捕获。这个新的上下文可能会有自己的合同预算实体,并且与上下文和预算上下文会有重叠,但也会有额外的关系和行为,这在那些其他上下文中是没有意义的。
这是一个非常难以解释的想法 :) 我曾经在这里回答了一个类似的问题:DDD - 如何设计不同有界上下文之间的关联

非常感谢。我考虑了一下,这让我想起了Evans所使用的书籍示例(存在不同的书籍类,并遵循书籍生命周期:草稿、编辑等)。但在这个示例中,我一直认为它是一个不同的逻辑实体(在数据库意义上是逻辑的:不同的表),并且使用域事件可以使这些实体保持同步。当你谈到逻辑实体时,你是指相同的数据库表,对吗? - Archange
@Archange 这是一种可能性,但存储库应该将这个细节抽象出来。当然,由于我们正在处理一个新的上下文,它应该有一个新的存储库。在数据库术语中,你可以说这个上下文封装了两个表之间的连接,但我们尽量不让数据库模式影响我们对领域的理解 :) - MattDavey
我同意你关于数据库抽象化的看法。我在思考微服务架构[Fowler]和从头开始重建微服务的能力。只要它与其他微服务的契约得到遵守,一切都很好。但是,如果我们使用一个新的存储库来连接数据库级别上的2个表来构建BC,那么我们在DB级别上不就有了高耦合吗:我们无法更改DB结构(重命名列)而不重新构建3个BC。我希望避免这种情况。也许我对此有所误解。 - Archange
1
如果你在面向对象编程中将事物封装到类中,那么在调用类的方法时,你会发现“耦合”无处不在,因为被调用者需要知道签名。这并不意味着使用封装是不好的,它只是意味着你已经从耦合逻辑转移到了“耦合”接口,因为这就是接口的作用。对于BCs来说,分享消息或基本DTO是可以的。这两个BC都不知道另一个BC正在使用它们。所以它们在高层面上是解耦的,这也是你想要的。但当然,它们仍然依赖于彼此的接口。 - C S

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接