我有一个WebApi后端有一个Blazor前端。该应用程序分为三个主要部分。核心、基础设施和演示。洋葱基本建筑结构。
Core.Domain域对象/实体Core.Application应用程序相关接口和Core.Application命令和处理程序
实现在Infrastructure.xxx中定义的接口的Core.Application项目。类似于用于数据库访问的Infrastructure.Persistence
Presentation.Backend The WebApi
Presentation.Frontend Blazor SPA
所以我有一个HTTP端点,它接受一个命令。它将被发送到处理程序,该处理程序将处理该命令并返回某种结果。
[HttpPost]
public async Task<IActionResult> Register([FromBody] RegisterCommand command)
{
var result = Mediator.Send(command);
if(result.Success)
{
return Ok(..);
}
return BadRequest(..);
}但是,当我想在用户成功注册后发送确认邮件时会发生什么情况呢?哪个部分负责触发邮件?
目前,我的命令处理程序负责这两个任务。但这感觉不对。命令处理程序应该只处理一个任务。
一种方法是某种事件聚合器,在这种情况下,这似乎是一种很好的方法。只需触发一个事件UserCreated,一些句柄就会处理它。我的意思是,我可以发布一个新的命令,这基本上是一样的。
但是,当我想等待我的第二个任务的结果时呢?我们去买点东西吧。这个例子可能不太准确,我只是想给你一些我想要避免的例子。
Handler(ShopOrderCommand command)
{
if(Validate(command) == ValidationResult.Suspicious)
{
MarkAsSuspicious();
return;
}
foreach(var item in command.ShoppingCart)
{
RemoveItemFromShopInventory(item);
}
if(command.IsGuestOrder)
{
RegisterTemporaryUser(..);
}
var id = CreateOrder(..);
CreateTransportLabel(id);
AssignPacker(id, command.Priority);
// ...
}发布于 2021-02-16 09:40:55
这里有一些提到的方法,所以我将简要介绍一下可能的方法。
对于触发和忘记操作,例如发送电子邮件通知,Mediatr (库)使用通知发布方法。这就像触发请求一样,我怀疑请求(没有返回值)和通知之间的区别是语义上的,而不是技术上的--只是通知不能发送返回值,而请求可以发送返回值。
我注意到您的代码并没有使用Mediatr库,但是它是如何处理这些问题的一个很好的灵感来源。我的建议是坚持尝试的和真实的,除非你有具体的理由去做不同的事情。
你已经提到了这个想法:
一种方法是某种事件聚合器,在这种情况下,这似乎是一种很好的方法。只需触发一个事件
UserCreated,一些句柄就会处理它。我的意思是,我可以发布一个新的命令,这基本上是一样的。
你说得对,这和发射调停人命令没什么区别。我认为这是有区别的,但这是语义上的,而不是技术性的。
CreateUser是一个命令。它包含创建用户的逻辑。UserCreated是一个通知/事件。它包含在创建用户之后应该发生的后续逻辑。因此,您可以使用SendEmail命令或UserCreated事件。两者都可以包含发送用户创建的电子邮件的逻辑。
你可能想两者兼用。当你发送多种电子邮件时,SendEmail太模糊了,你最终会得到各种各样的SendFooEmail,SendBarEmail,.命令。这可不太好。
类似地,如果您在创建用户之后采取了许多单独的操作,那么您将得到多个UserCreated事件处理程序,您同样需要将它们命名为UserCreatedFooHandler、UserCreatedBarHandler、.这也不可取。
通过使用这两种方法,您可以避免创建这些高度特定的名称。相反,您只需拥有一个UserCreated事件处理程序,它就会触发一个SendEmail命令。任何特定于用户创建的工作,例如生成邮件主体和主题行的内容,选择正确的收件人,.可以在事件处理程序中完成,而SendEmail则接受更广泛的输入(因此它的接口不特定于您发送的电子邮件类型),因此它可以在您希望发送电子邮件的所有实例中重用。
这有助于您保持事件处理程序和命令的名称简单,并且事件处理程序逻辑变得更容易阅读,因为它不包含实际的实现细节,而只是编排了在触发当前事件之后需要触发的后续命令。
如果您想要等待完成,有两种方法。要么是链式命令,要么是将低级逻辑抽象为服务。
再次使用Mediatr示例,链接命令相当容易,因为您的命令处理程序只需请求注入的IMediator依赖项,然后命令处理程序就可以在该对象上发布一个新命令。
本质上,它与发布通知(前面提到的)是一样的,但在这种情况下,您实际上能够接收返回值并根据它更改原始命令的行为。
相反,您可以将较低级别的逻辑抽象为服务。在大多数情况下,我都是这样做的。这只是一个域服务(或其他适当的层,例如数据层存储库)的问题,它处理命令中的子任务的部分细节。这样,你的命令就成了它自己责任的策划者。
还值得一提的是,为子任务使用服务并不少见,而且您可能已经这样做了,例如,当您查询存储库以获得命令需要处理的数据时。我在这里提到的是完全相同的东西,只不过所讨论的服务可以驻留在域/应用程序层而不是数据层。相同的想法,不同的层次。
据我所知,链接命令或为子任务创建服务的偏好主要是主观的选择。这取决于您是否希望减少命令的数量,如果您希望具体地使用命令而不是服务,.这些都是相对个人的(对代码基)的考虑,我不能提供一个普遍的答案。
https://softwareengineering.stackexchange.com/questions/422312
复制相似问题