因此,这是一个简单的工厂备件软件。由以下有界上下文组成,所有上下文都位于遵循干净架构的各自模块组件中。
所有内容都通过rest api公开。
-
帐户处理身份验证和其他帐户事务
-
SpareParts包含按类别组织的不同备件的存储库。还有一些关于命名和多语言支持的规则
-
检验用户可以创建新的检验,并可以在使用的地方添加备件。
我的问题是,如何选择零件并将其添加到检查对象中?我必须保留一份包括语言等所有部分的副本吗。还是不应该将它们视为单独的上下文?或者有其他设计?
理想情况下,每个服务/模块都应该是自主的。换句话说,它应该拥有完成工作所需的所有信息和业务逻辑。
因此,你有两个相关的上下文:;备件库存"";检查";。
我想你的备件库存会包括很多关于备件的可搜索信息。也许它包含了打印目录以供客户浏览所需的所有信息。
在"检验"上下文中,假设您正在手动在纸上写一张"检验单"。你需要在那张表上说明有关备件的哪些信息?
我假设这只是零件号、名称和数量。
在这种情况下,为了保持Inspections上下文的自主性,它应该有JUST该信息随时可用,而不是存储在库存中的整个备件对象数据。
如何同步?
一种选择是,在客户创建备件、删除备件或更改备件名称时使用的所有API中,在将命令发送到备件库存上下文后,将命令与数据子集一起发送到检验上下文,以保持数据在检验上下文中的同步。
一种更可持续的方式是,备件上下文在创建、删除或重命名零件时发布事件。
然后,Inspections上下文可以侦听这些事件,并相应地更新其本地存储。您可以发布具有足够信息的事件,以使事件的消费者满意(即零件号和名称),也可以发布Id,并让Inspections上下文的基础结构在本地Inspections域中执行更新之前回调Inventory API以获取相关详细信息。
在有界上下文之间使用事件可以防止领域知识泄漏到API中,但如另一个答案中所述,在谈论下一个上下文之前,在API中从一个上下文收集信息是一种快捷方式。但是,如果您的解决方案变得越来越复杂,那么它可能会在以后咬到您。
如果您在有界上下文中拆分应用程序,无论您如何实现它(单体或单独的服务),设计的关键部分是决定哪些信息进入有界上下文。如果你发现你需要在不同的上下文中复制信息,或者一个上下文需要从另一个上下文中获取信息来执行其工作,那么这表明你的边界可能不正确。
也就是说,通常情况下,有一个有界上下文会产生一些概念并给它们一个Id,而另一个上下文会保留这些Id的引用来与它们一起操作。在您的场景中,SpareParts上下文负责注册新部件(并为它们提供唯一的Id)。应用程序中没有其他上下文可以创建零件。但是您的Inspection上下文被允许在每次看到";PartRegisteredEvent";。
实际上,SpareParts和Inspections上下文都会有一个具有相同Id的零件列表,但是:
-
只有SpareParts上下文才能注册和注销(或您的域专家使用的任何更好的名称)
-
与存储在每个上下文中的部件相关的信息将是不同的。例如,在SpareParts上下文中,您将获得名称、翻译、图像等。在Inspections上下文中,将获得在检查中使用零件所需的任何信息。您最终可以拥有一个包含零件及其库存数量列表的Inventory上下文,以及另一个包含部件及其价格列表的上下文。
通常接下来的问题是:
-
如何在单个屏幕中向用户显示零件的所有信息?答案是视图或数据组合。要么用";小部件";其从其对应的上下文获取数据,或者使API从每个上下文获取数据并构造单个响应对象。
-
每个有界上下文如何收集他们需要的信息?有两种方法:a)使用事件:PartRegistered、PartUnregistered等。
Bonus方法:有一种快捷方式在单体中运行相对良好,可以避免使用事件在每个有界上下文中维护零件列表。在您的场景中,当API收到将X部分添加到检验Y的请求时,您可以在API级别进行预检查,在SpareParts中验证X部分是否存在(如果存在),然后将请求转发给检验BC,而不是直接将请求发送给检验BC(要求检验BC知道X部分存在)。如果Inspections不知道Part X,它可以为其创建一个记录。通过这种方法,BC可以始终相信他们收到的数据是有效的,因为它已经在API级别进行了验证。但请注意,如果您将这种方法转换为微服务,它将为每个请求引入至少一个额外的微服务往返。
你有巨石吗?如果是,那么你至少有两个选择。
您可以遵循纯粹主义方法,因此您在另一个域中拥有一个域的所有相关信息的副本。复制是使用rest api完成的,因为这是三个域向外部公开自己的方式。何时执行此复制也是一个问题,使用此方法。你需要沟通每一个变化吗?然后你需要一种均匀的总线(外部)来通知每一个机会。您还需要一种方法来管理由于使用旧信息执行的操作而导致的不一致。
另一种方法是在基础设施层内部实现对来自另一个域的信息的管理(您使用的是干净的体系结构,所以它无论如何都不会污染域)。在那里,您将使用查询或其他域的基础结构层中的任何其他代码来显示当前域中的信息。简单地说,您允许在基础设施层与两个或多个域交互,以避免使用rest api实现所有需要的代码。在这里,你也可以选择直接从持久性中获取信息,而不需要它的副本
两者皆有可能。对于第一个,如果将来需要拆分项目,则工作已经完成。但你要为额外的代码付出代价。如果你选择第二个,你可以在整体中重用你已经实现的东西,而不需要暴露更多计划中的API。但是,如果将来需要拆分代码,则必须实现域之间通信的所有管理。
这真的取决于未来的计划。不管怎样,我还是建议保持简单。你可以有一个没有那么多开销的工作系统,并在未来需要时计划额外的工作。