首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何编排多个业务任务?

如何编排多个业务任务?
EN

Software Engineering用户
提问于 2021-02-15 22:07:22
回答 1查看 1.3K关注 0票数 -1

我有一个WebApi后端有一个Blazor前端。该应用程序分为三个主要部分。核心、基础设施和演示。洋葱基本建筑结构。

Core.Domain域对象/实体Core.Application应用程序相关接口和Core.Application命令和处理程序

实现在Infrastructure.xxx中定义的接口的Core.Application项目。类似于用于数据库访问的Infrastructure.Persistence

Presentation.Backend The WebApi

Presentation.Frontend Blazor SPA

所以我有一个HTTP端点,它接受一个命令。它将被发送到处理程序,该处理程序将处理该命令并返回某种结果。

代码语言:javascript
复制
[HttpPost]
public async Task<IActionResult> Register([FromBody] RegisterCommand command)
{
    var result = Mediator.Send(command);

    if(result.Success)
    {
        return Ok(..);
    }

    return BadRequest(..);
}

但是,当我想在用户成功注册后发送确认邮件时会发生什么情况呢?哪个部分负责触发邮件?

目前,我的命令处理程序负责这两个任务。但这感觉不对。命令处理程序应该只处理一个任务。

一种方法是某种事件聚合器,在这种情况下,这似乎是一种很好的方法。只需触发一个事件UserCreated,一些句柄就会处理它。我的意思是,我可以发布一个新的命令,这基本上是一样的。

但是,当我想等待我的第二个任务的结果时呢?我们去买点东西吧。这个例子可能不太准确,我只是想给你一些我想要避免的例子。

代码语言:javascript
复制
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);
    // ...
}
EN

回答 1

Software Engineering用户

发布于 2021-02-16 09:40:55

这里有一些提到的方法,所以我将简要介绍一下可能的方法。

对于触发和忘记操作,例如发送电子邮件通知,Mediatr (库)使用通知发布方法。这就像触发请求一样,我怀疑请求(没有返回值)和通知之间的区别是语义上的,而不是技术上的--只是通知不能发送返回值,而请求可以发送返回值。

我注意到您的代码并没有使用Mediatr库,但是它是如何处理这些问题的一个很好的灵感来源。我的建议是坚持尝试的和真实的,除非你有具体的理由去做不同的事情。

你已经提到了这个想法:

一种方法是某种事件聚合器,在这种情况下,这似乎是一种很好的方法。只需触发一个事件UserCreated,一些句柄就会处理它。我的意思是,我可以发布一个新的命令,这基本上是一样的。

你说得对,这和发射调停人命令没什么区别。我认为这是有区别的,但这是语义上的,而不是技术性的。

  • CreateUser是一个命令。它包含创建用户的逻辑。
  • UserCreated是一个通知/事件。它包含在创建用户之后应该发生的后续逻辑。

因此,您可以使用SendEmail命令或UserCreated事件。两者都可以包含发送用户创建的电子邮件的逻辑。

你可能想两者兼用。当你发送多种电子邮件时,SendEmail太模糊了,你最终会得到各种各样的SendFooEmailSendBarEmail,.命令。这可不太好。

类似地,如果您在创建用户之后采取了许多单独的操作,那么您将得到多个UserCreated事件处理程序,您同样需要将它们命名为UserCreatedFooHandlerUserCreatedBarHandler、.这也不可取。

通过使用这两种方法,您可以避免创建这些高度特定的名称。相反,您只需拥有一个UserCreated事件处理程序,它就会触发一个SendEmail命令。任何特定于用户创建的工作,例如生成邮件主体和主题行的内容,选择正确的收件人,.可以在事件处理程序中完成,而SendEmail则接受更广泛的输入(因此它的接口不特定于您发送的电子邮件类型),因此它可以在您希望发送电子邮件的所有实例中重用。

这有助于您保持事件处理程序和命令的名称简单,并且事件处理程序逻辑变得更容易阅读,因为它不包含实际的实现细节,而只是编排了在触发当前事件之后需要触发的后续命令。

如果您想要等待完成,有两种方法。要么是链式命令,要么是将低级逻辑抽象为服务。

再次使用Mediatr示例,链接命令相当容易,因为您的命令处理程序只需请求注入的IMediator依赖项,然后命令处理程序就可以在该对象上发布一个新命令。

本质上,它与发布通知(前面提到的)是一样的,但在这种情况下,您实际上能够接收返回值并根据它更改原始命令的行为。

相反,您可以将较低级别的逻辑抽象为服务。在大多数情况下,我都是这样做的。这只是一个域服务(或其他适当的层,例如数据层存储库)的问题,它处理命令中的子任务的部分细节。这样,你的命令就成了它自己责任的策划者。

还值得一提的是,为子任务使用服务并不少见,而且您可能已经这样做了,例如,当您查询存储库以获得命令需要处理的数据时。我在这里提到的是完全相同的东西,只不过所讨论的服务可以驻留在域/应用程序层而不是数据层。相同的想法,不同的层次。

据我所知,链接命令或为子任务创建服务的偏好主要是主观的选择。这取决于您是否希望减少命令的数量,如果您希望具体地使用命令而不是服务,.这些都是相对个人的(对代码基)的考虑,我不能提供一个普遍的答案。

票数 0
EN
页面原文内容由Software Engineering提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://softwareengineering.stackexchange.com/questions/422312

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档