具有域逻辑和管道的 CQRS 处理程序(C#)--通过 Derek Comartin 的视频进行讲解
命令查询责任隔离(CQRS)是 .NET 应用程序中一种强大的设计模式,有助于保持读写操作之间的明确分离。 这种分离使复杂的业务逻辑具有更好的可扩展性、可测试性和可控性。 然而,随着应用程序的增长,C# 中的 CQRS 处理程序可能会变得逻辑臃肿,从而增加维护和测试的难度。
在其关于"使用领域逻辑和管道清理臃肿的 CQRS 处理程序"的深入浅出的视频中,Derek Comartin 介绍了如何通过将逻辑转移到领域模型和构建管道来逐步管理命令逻辑,从而清理此类 CQRS 命令处理程序。在本文中,我们将详细探讨 Derek 的方法,并学习如何在 C# 中构建更具可维护性的 CQRS 实现。
臃肿处理程序的问题
Derek 首先强调了许多网络应用中的常见情况:您用 C# 打开一个 CQRS 处理程序,结果一团糟。 数据验证、授权、业务逻辑、状态转换、事件发布、日志记录--所有这些都纠缠在一起。
他使用调度货物的命令对象来说明这一点。 译员负责
访问数据存储以加载货物。
检查发货状态是否准备就绪。
更新状态(即更新数据)。
将更改保存回数据访问层。
发送电子邮件。
- 发布领域活动。
所有这些都可以在一个地方完成。 这违反了 CQRS 模式的初衷,其目标是分离关注点并提高性能和可维护性。
将领域逻辑移至数据模型
Derek 的第一步是将验证和状态转换逻辑移入数据模型。 他在 Shipment 类中创建了一个 Dispatch() 方法。 这就是现在的领域逻辑。
该方法不需要在处理程序中手动检查发货状态,而是将逻辑封装在该方法中,从而确保了数据完整性以及在触发调度时的一致行为。 这是在基于 CQRS 的应用程序中实施简洁架构的关键。
例如,任何调用 shipment.Dispatch() 的地方都会自动执行所有验证和状态转换。 这与 CQRS 设计模式相一致,有助于保持处理程序与域逻辑之间的明确分离。
集中逻辑的价值
Derek 指出,这种修改并不是要增加不必要的抽象。 在翻译过程中,我们不能一概而论,而是要将应用程序代码中不同部分使用的逻辑集中起来。 如果需要多个命令处理程序来调度货物,那么这些自定义逻辑应放在一个地方--领域模型内部。
这将使您的数据模型更加健壮,CQRS 处理程序 C# 实现更加简单、更易维护。
介绍管道模式
为了进一步清理命令处理程序,Derek 引入了管道模式。 这种结构将命令处理为一连串小的、单一目的的步骤,每个步骤使用一个上下文对象并调用下一个步骤。
这在概念上类似于 ASP.NET Core 中间件,每一步都侧重于流程的特定部分:
检索货物(即读取数据)
调度(执行写操作)
出版活动
- 保存到数据存储
这些步骤使用一个共享命令对象,在管道中流动。这就为命令和查询责任隔离创建了一个简洁的模块化实现。
管道实现示例
在他的实施示例中,Derek 用以下步骤构建了流水线:
装运--使用存储库从数据访问层提取数据。
调度货物--调用 Dispatch() 方法应用领域逻辑。
添加域事件 - 将 "ShipmentDispatched "事件附加到上下文中。
发布事件--派遣事件通知外部系统。
- 保存更改 - 将更新持久化到数据存储。
每个步骤都代表一段不同的命令逻辑,加强数据验证并保持责任分明。
Derek 还指出,现在电子邮件通知是通过对域事件做出反应来单独处理的。 这符合事件源原则,并促进最终的一致性。
测试和可维护性优势
这种模式的最大优点之一是可测试性。 对于大型命令处理程序,您可能会有多个依赖项(如存储库、邮件服务、日志记录器)。 但是,当您将处理程序分解为流水线步骤时,每个步骤只需要几个依赖项。
这种模块化方法允许您使用依赖注入轻松地测试各个步骤,并在需要时使用伪造或模拟。 例如,如果您正在测试一个调用 Dispatch() 的步骤,您不需要模拟电子邮件服务或事件发布器。
这种关注点分离遵循了责任分离 CQRS 模式,使您的读写模型更简洁、更集中。
可组合性和可重用性
管道式方法的另一个好处是它具有可组合性。 如果使用类似于发件箱模式(Outbox Pattern)的东西,就可以确保只有在写模型被持久化之后才会发布事件。 这种控制水平在 CQRS 实施中至关重要,因为一致性和交付保证非常重要。
您还可以在不同的 CQRS 处理程序中共享步骤--例如,通用的 "SaveChanges "步骤或 "ValidateRequest "步骤。
利用支持命令和查询处理的 MediatR 库等工具,您甚至可以在 .NET Core 应用程序中使用 IServiceCollection 服务,通过依赖注入注册这些步骤。
要建立这个系统,您可以通过 Visual Studio 的软件包管理器控制台运行 Install-Package MediatR--这是在 C# 中实现 CQRS 的常见步骤。
权衡利弊
Derek 并不回避这种方法带来的复杂性增加。 管道引入了间接性,当您查看调用堆栈时,感觉就像在迷宫中穿梭。
不过,对于复杂的业务逻辑而言,这种权衡往往是值得的。 如果一个处理程序有 10 多个依赖项和数百行逻辑,CQRS 可以帮助开发人员更好地构建和维护这些流程。
关于何时重构的最终想法
最后,Derek 提醒观众仔细考虑他们的 CQRS 处理程序 C# 实现是否真的臃肿。 并非每种情况都需要管道。他的目标是说明各种可能性,而这取决于开发人员对自己的 CQRS 实施情况进行评估,并确定这种模式是否会有所帮助。
他鼓励开发人员关注代码中的各个领域,在这些领域中,关注点分离将有助于保持一致性,使代码更加模块化,并更好地管理读写操作--尤其是在 CQRS 网络应用程序中。
结论
Derek Comartin 的视频提供了使用域逻辑封装和管道清理 CQRS 处理程序的实用指南。 这种方法有助于解决代码臃肿的问题,促进数据完整性,并通过将应用代码分解为不同的模型来提高可维护性。
无论您是要处理员工数据、产品详情还是新的用户命令,通过管道和领域驱动设计应用 CQRS 模式都将使您的代码库更具可扩展性、可测试性和健壮性。
通过使用数据传输对象、独立的模型以及保持读写逻辑之间的明确分离,您的 .NET 应用程序将结构更合理,更易于随着时间的推移而不断发展。



