简体   繁体   English

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

[英]ddd - How to separate bounded contexts and share events?

I am actually reading a book called "DDD in PHP", to help me understand the Domain Driven Design. 我实际上正在阅读一本名为“DDD in PHP”的书,以帮助我理解Domain Driven Design。 So far everything is good, but I struggle to understand how to implement one specific topic without coupling Bounded Contexts: Domain Events 到目前为止一切都很好,但我很难理解如何在不耦合有界上下文:域事件的情况下实现一个特定主题

Let's say I have to BCs : 假设我必须参加BC:

  • Payments : Handles generation of invoices, sending them to the customers, etc 付款 :处理发票的生成,发送给客户等
  • Orders : Handles the creation of orders, their state, etc. 订单 :处理订单的创建,状态等。

When an Order is placed, an OrderCreated Event is dispatched. Order ,将调度OrderCreated事件。 The Payments BC catches this event with a subscriber, and creates the invoice. Payments BC通过订户捕获此事件,并创建发票。

The problem is, If I want to perfectly separate both BCs, where should the OrderPlaced Event live, since it's used by both BCs ? 问题是,如果我想完全分离两个BC, OrderPlaced事件应该在OrderPlaced存在,因为它被两个BC使用? Should it live outside both BCs ? 它应该住在两个BC之外吗? In both of them ? 在他们两个? What if I want to deploy the Invoices module as a standalone, without having access to the Orders module, and its OrderPlaced event definition, would it cause some fatal errors ? 如果我想将Invoices模块作为独立部署而无法访问Orders模块及其OrderPlaced事件定义,会导致一些致命错误,该怎么办?

Thank you in advance for the answers ! 提前谢谢你的答案!

The problem is, If I want to perfectly separate both BCs, where should the OrderPlaced Event live, since it's used by both BCs ? 问题是,如果我想完全分离两个BC,OrderPlaced事件应该在哪里存在,因为它被两个BC使用? Should it live outside both BCs ? 它应该住在两个BC之外吗? In both of them ? 在他们两个?

I would store the event in the context that owns it, ie the Orders context. 我会将事件存储在拥有它的上下文中,即Orders上下文。 How do you intend on separating the contexts? 你打算如何分离上下文? is it a physical / network boundary separation, or just conceptual? 它是物理/网络边界分离,还是只是概念?

What if I want to deploy the Invoices module as a standalone, without having access to the Orders module, and its OrderPlaced event definition, would it cause some fatal errors ? 如果我想将Invoices模块作为独立部署而无法访问Orders模块及其OrderPlaced事件定义,会导致一些致命错误,该怎么办?

It depends on what you are doing with OrderPlaced. 这取决于你在OrderPlaced上做了什么。 If you are subscribing to it from some sort of event stream, then reacting to it inside InvoicesBC by way of converting it to an internal-to-invoices concept, then you'll probably be fine as you could just not deploy the subscriber. 如果您从某种事件流订阅它,然后通过将其转换为内部到发票概念的方式在InvoicesBC内对其做出反应,那么您可能会很好,因为您可能无法部署订阅者。 If your code in InvoicesBC can run without having to know about OrderPlaced then you should be fine 如果InvoicesBC中的代码可以运行而无需了解OrderPlaced,那么您应该没问题

In general, there are a few ways to deal with this problem: 一般来说,有几种方法可以解决这个问题:

  1. Share a common definition. 分享一个共同的定义。 In C#, (not sure what the equivalent would be in PHP) in the past I've had a separate class library in the BC for the events that another BC might need (ie a MyContext.Contracts dll) that can then be referenced by other BC. 在C#中,(不确定PHP中的等价物是什么)过去我在BC中有一个单独的类库,用于另一个BC可能需要的事件(即MyContext.Contracts dll),然后可以通过它来引用其他BC。 These were published as internal nuget feeds (package manager stuff) so other contexts could keep themselves up to date. 这些是作为内部nuget feed(包管理器的东西)发布的,所以其他环境可以让自己保持最新。
  2. Weak serialization on the subscribing end. 订阅端的序列化较弱。 If you are subscribing to an event stream, you can deal with the raw representation it is stored in (ie JSON) rather than have some library automatically deserialize it into an object. 如果您正在订阅事件流,则可以处理它所存储的原始表示(即JSON),而不是让某些库自动将其反序列化为对象。 If you go down this route, I'd suggest some "contract tests" in the publishing side that mimic what a subscriber would do. 如果沿着这条路走下去,我建议在出版方面进行一些模拟订户会做什么的“合同测试” This will protect you from breaking your contracts with the outside world. 这将保护您免受违反与外界的合同。

Although an answer has been accepted I would still like to add my opinion :) 虽然答案已经被接受,但我还是想加上我的意见:)

They way your current BCs integrate is by subscribing directly to the messages published: Payments subscribes to OrderCreated . 他们当前BCs集成的方式是直接订阅发布的消息: Payments订阅OrderCreated This style of interaction is called choreography . 这种交互方式称为编排 It isn't necessarily bad but you have to weigh the pros and cons. 这不一定是坏事,但你必须权衡利弊。 For instance, when you need to add a step in-between then you may have the Address BC subscribe to to the OrderCreated so that it can validate the delivery address. 例如,当您需要在中间添加一个步骤时,您可以让Address BC订阅OrderCreated以便它可以验证传递地址。 Now the Payments BC has to subscribe to the AdressValidated event. 现在, Payments BC必须订阅AdressValidated事件。 But then again, this event is published quite a bit since we also use it when registering customers. 但是再一次,这个事件发布了很多,因为我们在注册客户时也使用它。 mmm... 嗯...

Another option is to use orchestration where you explicitly store the state of the process in question. 另一种选择是使用编排 ,您可以在其中明确存储相关进程的状态。 You my have an OrderProcess and a CustomerOnboardingProcess . 你有一个OrderProcess和一个CustomerOnboardingProcess You could then have your current BCs handle only messages within their own BC. 然后,您可以让当前的BCs仅处理其BC内的消息。 Another process BC would then co-ordinate the messages and keep track of the state. 然后BC的另一个过程将协调消息并跟踪状态。

Two other issues one may run into with choreography are: 编舞可能遇到的另外两个问题是:

  • Having to wait for human intervention 不得不等待人为干预
  • Parallel processing where two tasks are running for the same process and one needs to bring the results back together again. 并行处理,其中两个任务正在为同一个进程运行,另一个需要将结果重新组合在一起。

I hope that makes sense. 我希望这是有道理的。

The question is if the Payments BC must create the invoice, because as the name suggests, it's concern should be payments and not orders or invoices . 问题是, Payments BC是否必须创建发票,因为顾名思义,它应该是payments而不是ordersinvoices

Maybe something like this: 也许是这样的:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM