我正在实现有界上下文之间的交互,我发现它“以某种方式”削弱了开放封闭原则,我不确定这是设计 BC 和要考虑的常见权衡的自然结果还是我的设计失败。
考虑 Shop BC,您可以在其中使用一些商品从购物车中创建订单。创建的订单由OrderItem
s 组成,它们中的每一个都包含各种类型的ItemSpecification
值对象接口中的一种,例如ProductSpecification
or FooServiceSpecification
,保护不变量并包含一些数据。创建 Order 时,会发出任何其他 BC 可以侦听的异步事件。
该异步事件是从 Order 中创建的,并表示为(序列化的)OrderCreatedEvent 对象,包含 OrderDTO 对象,全部放置在与每个 BC 共享的 Core 命名空间中,因此任何 BC 都可以依赖 Core,但不能依赖其他方式。到目前为止一切都很好,几乎:
该 OrderItemDTO 必须包含 interface ItemSpecificationDTO
,需要为每种类型的规范实现。我的ItemSpecification
VO(与任何其他 VO/Entity in Order 一样)具有toCoreDTO()
实用地实现简单翻译的方法,并且它也使得实现新的相对困难ItemSpecification
并且忘记根据 DTO 实现。那应该没问题。
但是听那个事件的其他 BC 呢?在每个 BC 中,该事件需要在其反腐败层中进行转换,并且 BC 可能只对某些类型感兴趣ItemSpecificationDTO
并将它们转换为各种值对象,这对于特定的 BC 很重要。
正如鲍勃叔叔所说的关于 OCP 的机智:
您应该能够扩展系统的行为而无需修改该系统。
但是当我实现新类型时ItemSpecification
,对于每个可能对这种新类型感兴趣的 BC,我需要专门从 CoreDTO 翻译那个新类型(好吧,我可以编写一些抽象来在每个 BC 中进行翻译,所以我仍然只是添加代码而不修改任何东西,比如添加 if($x instanceof X))。但是,通过添加新类型,ItemSpecification
我需要在其他 BC 中进行适当的扩展(甚至可能修改某些内容,因为我们并不生活在理想世界中)。
我不知道该怎么想。这是整个 DDD 方法的缺点吗?或者也许确实是功能,因为在其他 BC 中寻找需要进一步扩展的内容、地点和方式是由领域需求而不是技术问题驱动的?似乎是对的。最后,我正在尝试进行领域驱动设计:-) 但在我看来这也有点危险。恐怕有一天我们会忘记更新其他 BC 并发生一些不好的事情。但这可能是因为我也扮演着领域专家的角色,“恐惧”应该属于这个角色。我的问题只是坐在两把椅子上还是我做错了什么?:-)