使用DDD和CQRS如何处理领域逻辑重复问题

15
我正在尝试使用DDD + CQRS,并且我不知道如何处理这些领域逻辑复制的问题:
首先,关于跨域的复制:
场景1:假设我有一个应用程序来处理办公室员工。 我有3个限界上下文:程序员部门,QA部门和审计部门。 每个BC都有自己的AR:“Programmer”,“Tester”,“Worker”。 它们99%不同,每个AR中有不同的逻辑,但是每个AR都有“姓名”,“姓氏”和一个简单的方法“getFullName”,该方法将这两个名称连接起来。
问题1:我应该如何避免AR中重复的常规方法? 最简单的答案可能是创建一些共享的“Human”类,并使这3个AR从中派生,但这与DDD的思想相违背,因为“QA Department”可能永远不需要“getFullName”方法,但需要其他“共享”方法。 因此,这种解决方案会使域受到未使用方法的垃圾邮件。
现在关于CQRS代码复制:
场景2:数据库包含发票。每张发票都有“总数”和“税收”字段。在“显示发票”页面中,我需要显示带税收的发票总额。因此,在我的读模型中,我需要执行“total = sum + tax”才能向最终用户显示它。但是,用户可以按“批准”按钮,该按钮应该在某个其他数据库(会计或其他)中注册发票金额。因此,在我的写模型中,我将再次需要执行“total = sum + tax”。
问题2:我应该如何避免这种重复? 当然,这是一个简单的情况,但是在分析一些我的实际应用程序后,我发现使用CQRS将需要在不同的地方进行大量的重复,因为有很多地方的最终结果都是从存储在数据库中的数据中计算出来的,并且这是在查询和命令操作中完成的。
任何想法吗? 我错过了什么吗?

场景1 - 如果同一个方法在3个有界上下文中都存在,我只会进行复制,因为每个上下文可能有不同的更改原因。 - Adrian Thompson Phillips
2个回答

6

场景1

  1. 将代码复制并粘贴到三个有界上下文中。
  2. 为共享库中包含的名称创建一个值对象,封装获取全名的逻辑。
  3. 创建一个员工有界上下文,负责管理员工详细信息。任何其他有限上下文都可以使用此来查找员工的详细信息。将发布事件以确保有界上下文之间的一致性(例如,包含他们全名的EmployeeJoinedCompanyEvent)。

场景2

任何计算都应该是您的领域模型的一部分。包含在实体、值对象或领域服务中。

任何计算的结果 - 例如本例中的总数 - 随后包含在从域发布的事件中。读取和写入数据存储可以从已发布事件中包含的值进行更新。它们不应该自己进行任何计算。

例如,如果域发布InvoiceApprovedEvent,则会包含所有必要的读取模型数据,包括税款和总额。

事件也是有界上下文之间集成的主要手段。因此,如果您需要更新会计有限上下文或外部系统,则应订阅来自发票有限上下文的相关事件,并在接收到事件时处理事件。

参考资料

以下是我强烈推荐实施DDD和CQRS的几个资源(假设您已经熟悉Eric Evan's DDD书)。


2
谈论场景2:读模型的整个理念不就是完全不使用您的领域层吗?本质上,它应该尽可能接近数据库,以跳过所有领域层的开销,并以所需的方式查询所需的数据。 - bezmax
关于方案1:看起来是合理的,正如Adrian在他的评论中提到的那样,它们看起来相似但可能本质上不同。 - bezmax
1
对我来说,情景2看起来不像是纯粹的读模型。将总和写入会计数据库并不是一项无辜的行动——您选择将其标记为“批准”,这可能意味着它在您的通用语言中有自己的位置。会计软件可以根据您批准的值做出决策,而发票中的未批准更改同时发生。如果您想要一个单一的真相来源来跟踪发生了什么,最好有一个包含总和的“InvoceTotalApproved”领域事件。 - guillaume31

3

场景1

很多时候,像主数据管理这样的东西将成为自己的有界上下文。这是员工姓名、出生日期等信息所属的地方。其他有界上下文以及各种读取模型可以直接或间接地从那里检索它们的数据。

场景2

如果你只是创建简单的总和,我不介意在不同的上下文中有一些重复。一旦你拥有更复杂的计算方法,它们应该明确与其相应的有界上下文相关联。在你的例子中,可能会有一个发票有界上下文,是发票相关算法、计算和服务的自然场所。然后,发票的创建和批准将从那里传播到受影响的读取模型。


看起来你对方案1的想法是正确的,那似乎是最合理的方法。至于方案2,我仍然不太确定应该如何实现。假设我想要一个表格,列出应用程序生命周期内所有发票的总和,并进行分页排序。控制器获取pageNum参数和sortBy参数,然后将其传递到哪里?直接传递到某个"Invoicing"区块链的存储库中吗? - bezmax
例如,可能会有一个报告区块链(Reporting BC),它汇总并提供这些信息。 - Dennis Traub
1
但这是否意味着我需要为每个需要在查询时进行某种计算的表单单独创建一个BC?例如,可能会有全局发票列表,其中包含3列,“上个月的发票”包含6列,并且在每个客户的“发票历史记录”中包含12列。这是否需要3个单独的BC,使用不同的发票填充不同的数据?然后我们又回到了场景1,因为我们有3个不同的BC,它们使用相同的规则计算其某些列,并使用自己的规则计算其自己的某些列。有什么想法吗? - bezmax

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接