【问题标题】:Domain Driven Design: How to deal with User Entities in different bounded context?领域驱动设计:如何在不同的有界上下文中处理用户实体?
【发布时间】:2017-03-23 16:44:56
【问题描述】:

我有一个关于领域驱动设计的问题。在我的应用程序的用户帐户/个人资料有界上下文中,有一个用户实体,其中包含帐户信息(id、用户名、密码、电子邮件、盐等)和个人资料信息(全名、头像、生日、性别等)。我还有另一个工作职位/申请的有界上下文,其中每个工作职位都有一个雇主用户,每个工作申请都有一个申请人用户。

问题是,工作有界上下文中的雇主/申请人用户是否应该与我用于用户帐户有界上下文的用户实体相同?或者我应该为雇主和申请人设计不同的用户类型实体?

如您所见,只有帐户有界上下文中的 id、全名、电子邮件和头像等信息与工作有界上下文相关。如果雇主/申请人与帐户/用户配置文件中的用户实体相同,它将加载更多无用的数据(不需要知道雇主/申请人的用户密码)。但是如果我为它们创建不同的实体类,这将使数据持久化更加棘手,因为在不同实体类中所做的更改可能会更改同一个数据库表中的相同数据。

你怎么看?我应该为所有用户实体使用一个用户实体,还是为不同的有界上下文/聚合使用不同的用户实体?如果后者是可取的,我该如何处理数据/实体持久性?

【问题讨论】:

  • 您的有界上下文共享同一个数据库?理想情况下,他们不应该。如果他们这样做,那么您需要明确哪个 BC 拥有哪条数据,并且只允许拥有 BC 更改该数据。
  • 我不明白你在说什么。它是相同的应用程序,只是不同的有界上下文。我总是使用相同的数据库,除非它是不同的应用程序。
  • BC 通常部署为微服务并拥有自己的数据库。
  • 很抱歉,我读了两本 DDD 书籍和网上很多关于 DDD 的文章,都没有说有界上下文必须有自己的数据库。我需要为您的声明提供一些引用。
  • 来源很多。 Source 1Source 2。只需查找限界上下文集成。通过共享数据库进行集成的情况非常罕见。

标签: entity domain-driven-design persistence aggregate bounded-contexts


【解决方案1】:

这是个老问题,你可能以某种方式解决了这个难题,但我会尽力回答。

你写的:

"仅来自 帐户有界上下文与工作有界上下文相关”

它们真的与 Job BC 相关吗?使用实体(甚至更多:使用聚合)您正在建模流程,而不是数据。在 Job BC 的哪个流程或用例中需要全名?该领域是否有规定不得雇用具有特定名字或姓氏的人?我假设不是。这么说你可能想到了,你必须显示一些屏幕,其中显示了一个人的全名。但屏幕不是进程,它们只是报告。您不应该通过报告/屏幕/UI 来驱动您的模型,而应该通过特定域中存在的流程来驱动您的模型。

好的,但是怎么做呢?显然,您仍然需要生成一些报告/屏幕。不是吗?答案是:你需要 CQRS (https://martinfowler.com/bliki/CQRS.html)。命令堆栈只是聚合等达到的流程模型。因此,您的构建块将由一些 ORM 持久化。它们的数据模型将由它们(构建块)的外观驱动。在这里使用一些可以轻松持久化甚至复杂聚合的 ORM,例如 Hibernate。

然后你必须建立查询堆栈。我看到了两种方法来实现它,它们取决于您是否在同一个数据库架构中拥有两个 BC。

当两个 BC 位于同一架构上时,只需使用一些数据库视图来为您的报告/屏幕构建数据。在这里使用一些非常快速和灵活的复杂查询 ORM,例如 MyBatis 或 Spring JDBC(如果您想与 JDBC 愤怒斗争,甚至可以使用普通 JDBC ;))。如果不是迫不得已,不要在这里使用 Hibernate,因为您会发现在这个堆栈中尽可能接近 SQL 是很好的。数据查询抽象将迫使您努力使用框架/ORM 来实现由聚合及其流程驱动的数据模型的复杂连接,而不是屏幕。另一个原因是,在普通业务应用程序中,读取比写入 DB 多几个数量级,在 CQRS 中,Query 堆栈的使用量比 Command 堆栈的使用量要多,所以这里需要一些快速的东西。

当 BC 使用不同的架构时,您需要构建报告数据库 (https://martinfowler.com/bliki/ReportingDatabase.html),它将“合并”两个架构。然后您可以对合并模式进行查看,问题简化为上述问题。

请注意,报告数据库为您提供了另一种扩展可能性:该数据库是只读的,因此可以在多个服务器之间轻松复制,您的查询堆栈服务可以成倍增加并隐藏在某些负载均衡器后面。

好的,但是电子邮件地址属性呢?您的域中可能有一个用例,应通知雇主或申请人在域上执行的某些操作/流程。 我认为处理此用例的域服务(或域事件处理程序)应该向其他 BC 的服务询问此特定用户的电子邮件(或广播将在某处处理的域事件)。甚至更好 - 它应该将这项工作委托给另一个 BC 中的某个通知服务。此通知服务将向 Account BC 的服务询问电子邮件地址。

在我看来,限界上下文(连同通用语言)是 DDD 最重要的思想。这是在建模过程中避免大泥球的真正强大工具。而且在实际领域中确定限界上下文真的很不容易:)

【讨论】:

  • 这是否意味着存在一个“用户实体”,其 ID 由不同的 BC 共享?
  • @Pepster 一般来说:是的(只是为了使数据关系可行)。详细说明:作业 BC 中不应有用户实体。 “用户”在本 BC 中没有任何意义。可能有“雇员”和“雇主”。他们可以从账户 BC 获得一些“用户”的 ID。匹配数据。
  • 如何欺骗我的大脑以这种方式思考? :) :) 谢谢你的回答。
  • 感谢您全面完整的解释
  • 这意味着您在不同的限界上下文中有 2 个同名的“用户实体”。虽然我认为用户名和密码应该在 Authentication Bounded Context 中,因为只有 Authentication BC 需要它们。
猜你喜欢
  • 2016-01-02
  • 2018-01-18
  • 2015-01-06
  • 2018-11-14
  • 2016-09-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-18
相关资源
最近更新 更多