ddd - 如何分离有界上下文和共享事件?

时间:2016-07-05 08:12:20

标签: php events domain-driven-design separation-of-concerns bounded-contexts

我实际上正在阅读一本名为“DDD in PHP”的书,以帮助我理解域驱动设计。到目前为止一切都很好,但我很难理解如何在不耦合有界上下文的情况下实现一个特定主题:域事件

假设我必须参加BC:

  • 付款:处理发票的生成,将其发送给客户等。
  • 订单:处理订单的创建,状态等。

当放置Order时,将调度OrderCreated事件。 Payments BC与订阅者一起捕获此事件,并创建发票。

问题是,如果我想将两个BC完全分开,那么OrderPlaced事件应该在哪里,因为它被两个BC使用?它应该住在两个BC之外吗?在他们两个?如果我想将Invoices模块作为独立模块部署,而无需访问Orders模块及其OrderPlaced事件定义,会导致一些致命错误,该怎么办?

提前感谢您的答案!

3 个答案:

答案 0 :(得分:2)

  

问题是,如果我想完全分离两个BC,OrderPlaced事件应该在哪里存在,因为它被两个BC使用?它应该住在两个BC之外吗?在他们两个?

我会将事件存储在拥有它的上下文中,即Orders上下文。你打算如何分离上下文?它是物理/网络边界分离,还是只是概念?

  

如果我想单独部署Invoices模块而无法访问Orders模块及其OrderPlaced事件定义会导致一些致命错误,该怎么办?

这取决于您使用OrderPlaced做什么。如果您从某种事件流订阅它,然后通过将其转换为内部到发票概念的方式在InvoicesBC内对其做出反应,那么您可能会很好,因为您可能无法部署订阅者。如果InvoicesBC中的代码无需了解OrderPlaced即可运行,那么您应该没问题

一般来说,有几种方法可以解决这个问题:

  1. 分享一个共同的定义。在C#中,(不确定PHP中的等价物是什么)过去我在BC中有一个单独的类库,用于另一个BC可能需要的事件(即MyContext.Contracts dll),然后可以通过它来引用其他BC。这些内容是作为内部nuget feed(包管理器的东西)发布的,因此其他环境可以保持最新状态。
  2. 订阅端的序列化较弱。如果您正在订阅事件流,则可以处理它所存储的原始表示(即JSON),而不是让某些库自动将其反序列化为对象。如果你沿着这条路走下去,我会在发布方面建议一些模仿用户会做什么的"contract tests"。这将保护您免受违反与外界的合同。

答案 1 :(得分:1)

虽然答案已经被接受,但我还是想加上我的观点:)

您当前的BCs集成方式是直接订阅发布的消息:Payments订阅OrderCreated。这种交互方式称为编排。这不一定是坏事,但你必须权衡利弊。例如,当您需要在中间添加一个步骤时,您可以让Address BC订阅OrderCreated,以便它可以验证传递地址。现在Payments BC必须订阅AdressValidated事件。但是再一次,这个事件发布了很多,因为我们在注册客户时也使用它。嗯...

另一种选择是使用 orchestration ,您可以在其中明确存储相关流程的状态。您有OrderProcessCustomerOnboardingProcess。然后,您可以让当前的BCs仅处理其BC内的消息。然后,另一个进程 BC将协调消息并跟踪状态。

编舞可能遇到的另外两个问题是:

  • 必须等待人为干预
  • 并行处理,其中两个任务正在为同一个进程运行,另一个需要将结果重新组合在一起。

我希望这是有道理的。

答案 2 :(得分:0)

问题是Payments BC是否必须创建发票,因为顾名思义,它应该是payments而不是ordersinvoices

也许是这样的:

Orders -> 'order created' -> Payments -> 'payment done' -> Orders -> 'invoice created'