Validation 异步SAGA模式中的验证-CQRS&;DDD 我们考虑下面的流程: API客户端调用[POST]/API/v1/invitation/:InvitationId/confirm 在一个传奇故事中确认邀请 最终引发一个邀请确认的事件以表明成功

Validation 异步SAGA模式中的验证-CQRS&;DDD 我们考虑下面的流程: API客户端调用[POST]/API/v1/invitation/:InvitationId/confirm 在一个传奇故事中确认邀请 最终引发一个邀请确认的事件以表明成功,validation,domain-driven-design,cqrs,saga,Validation,Domain Driven Design,Cqrs,Saga,我们很难找到一个好地方来验证我们传递给传奇的“事件”。例如,我们希望确保: -指定的邀请ID存在 -相应的邀请未过期或尚未处理 我们尝试了两种方法: 发出命令: 启动命令RequestInvitationConfirmation 同步处理此命令,如果该命令无效或引发邀请确认请求事件,则返回错误 流的其余部分是相同的 缺点: -要求我们遵循“请求/响应”模式(在HTTP请求生存期内同步) 提出一项活动: 提出一个事件邀请确认请求 在SAGA中,查询邀请服务并执行验证。如果命令无效,我们将发布

我们很难找到一个好地方来验证我们传递给传奇的“事件”。例如,我们希望确保: -指定的
邀请ID
存在 -相应的邀请未过期或尚未处理

我们尝试了两种方法:

  • 发出命令:

    • 启动命令
      RequestInvitationConfirmation
    • 同步处理此命令,如果该命令无效或引发
      邀请确认请求事件,则返回错误
  • 流的其余部分是相同的

    缺点: -要求我们遵循“请求/响应”模式(在HTTP请求生存期内同步)

  • 提出一项活动:

    • 提出一个事件
      邀请确认请求
    • 在SAGA中,查询
      邀请
      服务并执行验证。如果命令无效,我们将发布一个事件
      InvitationConfirmationFailed
      (……)
  • 缺点: -据我所知,应该用传奇来编排流程。这里我们介绍“验证”的概念。我不确定这是推荐的方法


    验证是一个非常常见的概念。在分布式完全异步系统中如何处理它?

    您有一个命令
    ConfirmInvitation
    包含
    邀请ID
    。您可以通过
    InvaitionAppService
    将其发送到您的
    邀请
    域。您的
    邀请
    域应该如下所示

    ...
    public void ConfirmInvitation()
    {
        if (this.Status == InvitationStatus.Confirmed)
            throw new InvalidInvitationException("Requested invitation has already been confirmed");
        //check more business logic here
        this.Status = InvitationStatus.Confirmed;
        Publish(new InviationConfirmedEvent(...));
    }
    ...
    
    您的
    InvitationAppService
    应该具有以下内容:

    ...
    public void ConfirmInvitation(Guid invitationId)
    {
        // rehydrate your domain from eventstore
        var invitation = repo.GetById<Invitation>(invitationId);
        if (invitation == null)
            throw new InvalidInvitationException("Invalid Invitation requested");
        invitation.ConfirmInvitation(new ConfirmInvitation(...));
    }
    
    。。。
    公开无效确认(Guid邀请ID)
    {
    //从eventstore重新水化您的域
    var invitation=repo.GetById(invitationId);
    如果(邀请==null)
    抛出新的InvalidVitationException(“请求的邀请无效”);
    邀请.确认书(新确认书(…);
    }
    

    您不需要引入新的事件
    邀请确认请求
    。DDD是一种域/业务验证应该驻留在域内的方法。不要尝试在您的领域中采用其他模式或技术。在saga中验证您的域(用于协调跨服务的分布式事务)可能会造成复杂性和混乱

    此系统设计中的重要一点是:“谁是此API的客户?”

    • 如果此客户端是内部
      服务
      应用程序
      ,那是一回事(如分布式应用程序、微服务等)
    • 如果API被第三方客户端使用,那是另一回事
    简短回答

    如果API在
    服务之间内部使用
    ,则在系统中发送Id无效的命令是错误的,因此系统开发人员应记录并检查该命令。同样,此类情况也应该通过手动方式解决(通过一些管理后端)。记录这些内容并通知开发人员

    如果API是从第三方应用程序使用的,那么API和它使用的系统的其他部分之间的责任如何划分就很重要了。让API负责验证,不要发送id无效的命令。将ID无效的命令视为错误,与第一种情况相同。在这种情况下,如果您使用异步流,您将需要一种与第三方应用程序通信的方式来通知它。你可以用类似的东西

    对于验证检查的第二部分和

    长话短说

    如果你四处搜索,你会看到很多关于错误和验证的讨论,下面是我对此的看法

    由于我们对系统的其他部分进行了分离,因此分离我们所存在的错误类型似乎是很自然的。你可以看看这个话题

    让我们定义一些错误类型

    • 域错误
    • 应用程序错误
    • 技术错误(数据库连接丢失等)
    因为我们有不同类型的错误,所以应该从系统的不同部分执行验证

    这些错误的通信也可以通过不同的机制完成,具体取决于:

    • 操作的请求者和接收者
    • 使用的通信通道
    • 通信类型:同步或异步
    现在,您进行的验证包括:

    • 验证具有指定的
      Id
      邀请是否存在
    • 验证
      邀请是否未过期
    • 验证邀请是否尚未处理(已接受、已拒绝等)
    如何处理这一问题将取决于我们如何在应用程序中分离责任。让我们使用这个原则并定义清楚的规则,每个层(域、应用程序等)应该期望从其他层得到什么

    让我们定义一条规则,即不应创建和调度包含与现有邀请不对应的邀请ID的命令

    注:此处使用的术语可能会因项目中使用的架构类型(分层架构、六边形等)而有很大差异

    这将强制
    CommandCreator
    在发送命令之前验证具有指定
    Id
    邀请是否存在

    在使用API的情况下,将接受请求的
    RouteHandler
    (应用程序控制器等)必须:

    • 请自行执行此验证