17

我正在使用 DDD 原则制作应用程序。在尽可能多地考虑了所有事情之后,我开始制作我的限界上下文。我还没有设置最终结构,但到目前为止,我的应用程序将包含以下有界上下文:

  1. 员工管理
  2. 购买
  3. 档案
  4. 报告

我希望它尽可能地可插拔,因此我可以例如单独开发和维护它们。他们可能会公开 WCF 或 Web API 以与他们交互。

我将使用Udi Dahans 实现一个简单的 CQRS 模式。我不想一直使用事件溯源、消息总线等,因为这不会是一个高度协作的应用程序(少于 1000 个用户,而且他们不太可能编辑相同的小数据集),这将增加了很多不必要的复杂性。

所以对于问题:

员工和部门实体对所有 BC 都是通用的,如何对其建模?

部门是组织结构的一部分,因此在员工管理 BC 中,员工在一个部门工作,他们可以管理一个部门,并且他们有他们工作过的部门的历史。

在采购 BC 中,商品从一个部门采购,然后交付给一个部门。供应商与不同部门有不同的合同。

在存档中,一些信息将被存档并绑定到一个部门,等等。

这几乎同样适用于员工。

如何持久化有界上下文中的数据?

它们可以映射到同一个数据库或每个都有自己的数据库。

到目前为止我的一些想法

如何建模
我应该再制作一个名为“公司”或“组织”的 BC 并在那里管理部门吗?

根据上面提到的 Udi Dahans 文章,我应该为每个 BC 创建一个部门实体和一个员工实体,其中包含我需要的 BC 字段和行为。这听起来很合理,但是我正在考虑如何实际使用它,但我无法弄清楚。我需要访问在其他地方管理的部门,但我究竟该怎么做而不是混合我的 BC?

如何使用?
假设我通过查询从某个地方获取了我的部门列表。在 UI 中,我得到了我也想购买的部门列表。这是该部门的第一次采购,因此采购部 BC 尚不知道该部门……因此采购部对象中的采购部对象将填充从另一个 BC 维护的数据 - 那么我该如何坚持呢?如果不存在,我需要添加一些信息,例如送货地址和发票地址?

在“注册部门 UI”中,我是否应该在所有 BC 上调用“RegisterDepartment”服务,然后确保这些与通过 UI(MVC 控制器)所做的所有更改同步?

对员工也是如此。我想知道哪个员工进行了购买或将某些东西放入档案中。因此,不知何故,我也需要这些 BC 中的员工对象,但从不同的 BC 管理它们。

持久
化 上面的一些挑战可以通过将不同的员工对象映射到数据库中的同一个表来解决。Purchasing BC 和 Archive BC 不能注册新员工,但会将信息附加到那里的员工,并将他们与同一数据库中的其他对象联系起来。然后数据库将确保所有 BC 仍然生活在同一个世界中......

我需要建议,所以我最终不会做出以后很难维护的东西。

4

2 回答 2

13

似乎您的大部分疑问都围绕着:“不同的有界上下文如何共享单个现实生活中的对象?”

问题是,尽管实体相同,但每个 BC 对它们的处理方式不同。在 Employee Management BC 中,整个权重集中在 Employee 和 Department 实体上——您应该能够添加、修改、相互分配、保留历史记录并处理有关管理的所有业务逻辑。您可以实施一些保留员工个人数据、维护适当官方结构或维护某些责任的政策。

另一方面,采购上下文中的部门实体仅表示例如发票地址,也可能是负责部门的人员,而兴趣中心将构建订单。所有与购买过程没有直接联系的数据都应该放在不同的环境中。例如,如果域要求每个订单都必须连接到部门并且缺少发票详细信息,则购买上下文不应尝试自行填写它们。相反,可以通知员工管理层来填补缺失的部分。

请注意,它很可能发生在同一个应用程序甚至同一个窗口中。但是您必须确保它将通过员工管理上下文发生,即通过调用上下文公共 API。

作为旁注,我不知道您的域,但您可能想要重新考虑您的上下文边界,例如将交付与购买分开。

继续使用并按照您的示例进行操作,如果您想购买,我会考虑以下路径:

  • 阅读必要的部门数据(让我们等到后面用“如何”),您可能想检查此时是否每个数据都存在
  • 阅读可以购买的商品,根据您的领域,可能值得介绍另一个 BC,例如供应商。以上都是CQRS的“查询”部分
  • 构造订单或任何其他必要的购买上下文实体,执行验证或任何其他逻辑
  • 提交更改,保留购买上下文实体(“命令”部分)
  • 创建和发布一些域事件(例如通知存档或报告)

最后但并非最不重要的一点是,您不应该关心域层中的全局持久性。每个 BC 都应该连接到一些数据访问或基础设施层,以提供必要的对象并处理诸如从何处获取它们等细节。

特别是实体不一定需要镜像数据库布局,存储在一个或多个数据库中的问题应该只是一个性能问题。例如,一些实体将引用相同的对象(例如员工姓名),但可以从完全不同的表或数据库中获取其他详细信息(即购买历史或发送到存档的元素)。您可以使用NHibernate 之类的东西来使其易于管理。

于 2012-11-23T18:18:19.337 回答
5

我在这里回答一个老问题,但我有另一个关于 OPs 问题中面临的问题的例子。

我有一种情况,我正在处理制造公司内使用的应用程序。该公司设有销售、运营、生产、客户、技术支持部门。在所有这些部门中都有一个客户的概念。因此,我很难弄清楚我如何在这些部门中拥有一个客户实体(我已将其绘制为我的绑定上下文)。

就在反复思考这个问题时,我想起了我在 Jimmy Bogard 的一篇博客文章中读到的评论,他谈到了他研究过的领域模型,该模型需要数周的工作才能完成,因为他们在这个问题上工作得越多,他们对领域的理解就越多,并且能够得出一个优雅的设计。

我的清醒时刻是当我停止思考持久性并停止思考这些 BC 中的客户时。

我意识到销售 BC 需要了解客户,但他们需要非常具体的信息,例如谁是 MD、联系人列表、办公室列表、决策者是谁、谁是财务联系人等。

现在,Ops BC 负责在系统中提出订单,他们需要一个客户的概念。但是,他们不需要知道 MD 是谁,或者联系人列表,以及谁在乎客户拥有哪些办公室?Ops 只需要知道客户名称,在我们的例子中是 4 个字符的客户代码。当我考虑到这一点时,我什至不需要将此信息作为实体保存,它可能只是我的 Ops BC 中的一个值对象。但是如何将这些信息输入到我的 Ops BC 中?嗯,这真的很简单。BC 定义了一个接口,我的应用程序可以通过该接口与 Ops BC 交互。我的应用程序不知道 Ops BC 内部发生了什么,但它确实知道有一个 Order Aggregatr 根,并且它有一个 RaiseNewOrder 方法,该方法接受一个 CustomerValueObject 类型的参数。CustomerValueObject 包含一个 4 字母代码和一个客户名称。因此,在我的 App UI 中,我可以使用 Sales(我们称之为客户管理)BC 来获取客户列表以呈现下拉列表。然后,当我处理操作的事务部分时,即用户发布新订单信息时,我使用 Ops BC 并传入 customerVO 作为参数。

因此,我的 Ops BC 和 Sales BC 是独立的,并且是独立的,它们在内部具有确保域完整性所需的概念,并且我能够在我的 UI 中访问我需要的数据,以便操作员可以选择客户然后在 Ops BC 中执行操作。

我意识到销售团队的客户与运营团队的客户不同。对于运维人员来说,客户只是他们用来标记订单的东西。他们对该公司的内部运作或任何有关它的信息没有任何兴趣。只要我对整个 BC 的客户都有一个通用 ID,我就有办法将数据拉回我需要用于演示的 ID。例如,我可以使用 Ops BC 拉回客户 XXXX 的订单列表。我不需要通过客户集合进入来做到这一点。

现在,把这个皱纹扔进去。销售人员有时需要了解客户下的订单,所以我是否需要开始在我的 Sales BC 中复制信息?这就是我卡住的地方,但后来我意识到我说的是销售人员,他们是 UI 的用户,在 UI 中我可以拉回给定客户的订单列表。

我对 DDD 还是很陌生,但我意识到 DDD 是关于与领域专家的对话,当你正确地进行这些对话时,你会意识到你认为存在于 BCs 中的实体......没有。

我认为我们作为开发人员通过经验设置为非规范化数据,并考虑数据库结构,以及我们如何将这些客户对象持久化回表......所以我们将实体视为一个东西,但域专家们不以这种方式看待这些实体。

为我达成交易的评论来自相当脾气暴躁的运营总监,当时我问了一个问题,比如“那么会计部门需要从这个订单中看到什么”,他的回答是“我不在乎什么”他们想要,我只想要我想要的”。我认为这就是你需要如何看待 BC。

我希望这个华夫饼对某人有帮助。

于 2016-02-02T11:58:28.333 回答