CQRS中应使用单条件命令还是多专用命令?
在CQRS架构中:单命令还是多专用命令?
核心权衡与决策建议
先直接给结论:优先拆分为多个专用命令,下面具体说原因和解决你担心的逻辑重复问题。
单个命令方案的问题
用一个GenerateAndSendReportToClient命令,处理器里加分支逻辑选择查询方式,看似省事儿,但会埋下不少坑:
- 违反单一职责原则:一个命令既要处理SSN查询的场景,又要处理TaxNumber的,后续再加个客户ID查询,处理器会变成堆满if-else的大泥球,维护起来头疼。
- 参数模糊:调用方要传多个可选字段,很容易出现误传(比如同时填了SSN和TaxNumber),你还得在处理器里加一堆校验逻辑判断哪个参数有效。
- 不符合CQRS的核心思路:CQRS就是要让命令的语义更明确,一个命令对应一个明确的业务动作,模糊的命令会让整个系统的意图变得不清晰。
多专用命令方案的优势
拆成GenerateAndSendReportToClientBySsn和GenerateAndSendReportToClientByTaxNumber,好处很明显:
- 职责单一:每个命令和处理器只干一件事,逻辑清晰,新人接手一眼就能看懂每个命令的作用。
- 参数严谨:调用方不会搞混,编译期就能避免传参错误,不用额外做校验。
- 扩展性好:以后要加按客户ID生成报告的需求,直接新增对应命令和处理器就行,完全不影响原有代码,符合开闭原则。
解决逻辑重复的问题
你担心的逻辑重复其实是伪问题,把生成和发送报告的核心逻辑抽成公共服务就行,两个处理器只负责各自的客户查询,然后调用公共服务完成核心动作。举个代码例子:
// 提取公共的报告生成服务 public class ClientReportService { public void GenerateAndSendReport(Client client) { // 这里放生成报告、发送给客户的核心逻辑 // 比如生成PDF、调用邮件接口等 } } // 按SSN查询的命令处理器 public class GenerateReportBySsnHandler { private readonly IClientRepository _clientRepo; private readonly ClientReportService _reportService; public GenerateReportBySsnHandler(IClientRepository clientRepo, ClientReportService reportService) { _clientRepo = clientRepo; _reportService = reportService; } public void Handle(GenerateAndSendReportToClientBySsn command) { var client = _clientRepo.FindBySsn(command.Ssn); _reportService.GenerateAndSendReport(client); } } // 按TaxNumber查询的命令处理器 public class GenerateReportByTaxNumberHandler { private readonly IClientRepository _clientRepo; private readonly ClientReportService _reportService; public GenerateReportByTaxNumberHandler(IClientRepository clientRepo, ClientReportService reportService) { _clientRepo = clientRepo; _reportService = reportService; } public void Handle(GenerateAndSendReportToClientByTaxNumber command) { var client = _clientRepo.FindByTaxNumber(command.TaxNumber); _reportService.GenerateAndSendReport(client); } }
这样一来,核心逻辑只写一次,两个处理器的代码非常简洁,完全没有重复。
例外情况
如果你的业务场景非常简单,短期内绝对不会新增其他查询条件,而且调用方的使用场景单一,也可以暂时用单命令方案,但一定要做好参数校验和注释,一旦有扩展需求,立刻重构为专用命令。
内容的提问来源于stack exchange,提问作者Nikola
相关产品推荐
相关产品推荐

