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

Php ddd-如何分离有界上下文和共享事件?,php,events,domain-driven-design,separation-of-concerns,bounded-contexts,Php,Events,Domain Driven Design,Separation Of Concerns,Bounded Contexts,实际上,我正在读一本名为“PHP中的DDD”的书,以帮助我理解域驱动设计。到目前为止,一切都很好,但我很难理解如何在不耦合有界上下文的情况下实现一个特定主题:域事件 比方说,我必须遵守BCs: 付款:处理发票生成、发送给客户等 订单:处理订单的创建、状态等 放置订单时,将调度OrderCreated事件。 PaymentsBC通过订户捕获此事件,并创建发票 问题是,如果我想完全分离两个BC,那么既然两个BC都使用它,orderplacted事件应该在哪里活动?它应该住在两个BC之外吗?在他们

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

比方说,我必须遵守BCs:

  • 付款:处理发票生成、发送给客户等
  • 订单:处理订单的创建、状态等
放置
订单
时,将调度
OrderCreated
事件。
Payments
BC通过订户捕获此事件,并创建发票

问题是,如果我想完全分离两个BC,那么既然两个BC都使用它,
orderplacted
事件应该在哪里活动?它应该住在两个BC之外吗?在他们两个人身上?如果我想将Invoices模块作为独立模块部署,而不访问Orders模块及其OrderPlaced事件定义,会不会导致一些致命错误

提前感谢您的回答

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

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

如果我想将Invoices模块作为独立模块部署,而不访问Orders模块及其OrderPlaced事件定义,会不会导致一些致命错误

这取决于您对OrderPlaced所做的操作。如果您是从某种事件流订阅它,然后在InvoicesBC中通过将其转换为内部到发票的概念来对其作出反应,那么您可能不会有问题,因为您无法部署订阅服务器。如果您在InvoicesBC中的代码可以运行,而不必知道OrderPlaced,那么您就可以了

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

  • 共享一个共同的定义。在C#中(不确定PHP中的等价物是什么),过去我在BC中有一个单独的类库,用于另一个BC可能需要的事件(即MyContext.Contracts dll),然后可以被其他BC引用。这些内容被发布为内部nuget提要(PackageManager内容),以便其他上下文能够保持最新
  • 订阅端的弱序列化。如果订阅事件流,则可以处理它存储在其中的原始表示(即JSON),而不是让某个库自动将其反序列化为对象。如果你沿着这条路线走下去,我建议在发布端使用一些类似订阅者的方法。这将保护你不违反与外界的合同
    问题是
    付款
    BC是否必须创建发票,因为顾名思义,它应该关注
    付款
    ,而不是
    订单
    发票

    也许是这样的:

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

    虽然答案已被接受,但我仍想补充我的意见:)

    您当前BCs整合的方式是直接订阅发布的消息:
    Payments
    订阅
    OrderCreated
    。这种互动方式被称为编舞。这并不一定是坏事,但你必须权衡利弊。例如,当您需要在两者之间添加一个步骤时,您可以让
    地址
    BC订阅创建的
    订单
    ,以便它可以验证交付地址。现在,
    Payments
    BC必须订阅
    addressvalidated
    事件。不过,由于我们在注册客户时也会使用它,所以这次活动的发布量很大。嗯

    另一种选择是使用编排,在其中显式存储相关流程的状态。您有一个
    OrderProcess
    和一个
    CustomerOnboardingProcess
    。然后,您可以让当前BC仅处理其自己BC中的消息。然后,BC的另一个进程将协调消息并跟踪状态

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

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

    我希望这是有意义的。

    也许这是一个不好的例子,但逻辑保持不变,我应该将跨多个BC使用的事件定义放在哪里?如果BC需要侦听事件,它还需要触发它的对象。因此,我不确定您是否需要过于担心模块化,因为您将始终包含引发事件的对象。但我不知道你说的“过去的”是什么意思。你的意思是BC必须能够触发事件还是监听事件?我的意思是OrdersBC必须触发OrderCreated事件,PaymentsBC必须监听它,因此它们确实是因为这个事件而耦合的。正如tomliversidge所说,为OrdersBC.Events创建一个单独的库的想法可能是好的,但我仍然担心模块化。我想在一个基础设施上部署OrdersBC,或者在其他地方部署PaymentsBC,或者根本不部署,但是你应该问问自己:如果PaymentsBC甚至不知道上下文,它怎么知道OrderCreated事件?也许你应该看看两个独立的项目,比如通过套接字进行通信。同样,对PHP不太清楚,但在C#world中,如果你从一个独立的库导入了对dll的引用,你仍然可以部署一些东西。这与导入库来帮助解析JSON是一样的。您只需部署特定的BC代码,外加少量编译代码