如何在 DDD 中设计多阶段任务

How to design multistage tasks in DDD

大约在这一年,我们重构了后端代码库,使架构更符合 DDD,我们还在中实施了 Hexagonal architecturePorts & Adapters 模式的组合我们的大部分服务。到目前为止,我们的大多数用例都非常简单直接;所以使用简单的实体和 Command and Query 处理程序等效果很好。但是随着软件功能的增长,我们现在需要更复杂的结构。

所以我们想要实现一些我能给出的最好描述是 multistage tasks 的功能。例如,我们有一个注册和登录的两步验证:用户输入他们的信息,然后一个验证码被发送给他们(通过短信),他们必须输入它才能获得令牌。或者对于某些功能,我们需要获得用户的驾驶执照和照片等,然后管理员必须验证它们并向用户发送通知,然后用户必须进行付款等等。

我们观察到我们不能像简单的 entitiesCQRS 模式一样处理这种特性(任务?),但我们也未能找到一种好的和惯用的方法来实现这些特性.因此,我们正在寻求一些信息和指南来帮助我们。

广泛的方法是将一个长 运行 的进程建模为一个 saga,它可以跟踪它的状态(持久:事件溯源在这里通常非常有用,因为持久化的事件给你审计日志记录和可观察性几乎是免费的),同时向其他服务发送命令(以及接收命令)。 saga 可以自动从故障中恢复,使系统处于定义的状态(理想情况下 good/functional 状态,尽管有时让系统无法运行并发出需要修复的信号可能很有用...)。

例如,您可以将未经验证的用户建模为传奇,其中状态可能是:

  • 使用个人资料创建,未发送短信
  • SMS 已发送并需要代码(保留个人资料信息)...可以从此发送带有新代码的 SMS,但会使旧代码无效
  • 已验证(可能不需要验证用户 ID 以外的个人资料信息)

在前两种状态中的任何一种状态下,“发送验证短信”命令都是有效的,并且会转换到第二种状态(在生成代码并发送短信之后)。在第二个状态下,可以验证“来自 SMS 的验证代码”命令,如果有效,则转换到第三个状态(在转发配置文件信息之后,以便可以创建具有该配置文件信息的经过验证的用户)。

基本上,您可以将 saga 视为“待办事项列表”(有些与 DDD 相邻的学校更喜欢这个术语)。与许多 DDD 一样,思考如何在不使用计算机的情况下解决问题。对于 ID 验证过程,您可能会有一些 sheet 带有清单的纸,最终会附加到用户的文件中;这类流程已经 survived/thrived 存在了很长时间,因此有充分的理由将它们的各个方面纳入我们的软件设计。

大多数聚合在某种意义上都可以被认为是有限状态机。

聚合通常涉及相当复杂的工作流,这些工作流循环通过许多(但有限的)状态,每个转换都与定义明确的业务规则相关联。

在您的示例中,User 记录在 REGISTERED、VERIFIED、ADMIN APPROVED、PAID 等状态之间移动。这也让您可以自由地从任何状态转换到任何状态,允许您添加随着时间推移的新规则(从不付款的折扣用户、无需验证的受邀用户等)

状态机内置于聚合中。命令启动转换。命令处理程序调用聚合方法执行转换并在持久化之前更改聚合状态(并引发事件,如果有的话)。

附带说明一下,这是一个很好的启发式方法,可以判断您的应用是否适合使用 DDD/CQRS 模式。对于像 CRUD 操作这样的简单情况,状态机没有意义。大多数 CRUD 记录会在 ACTIVE-ARCHIVED 或 CREATED-UPDATED-DELETED 状态之间循环。

阅读更多: