【问题标题】:external service result mutates state of aggregate外部服务结果改变聚合状态
【发布时间】:2021-06-24 13:14:05
【问题描述】:

我的问题是我不知道如何处理改变状态但在执行之前还需要验证的外部调用

这是我的命令处理程序

public async Task<IAggregateRoot> ExecuteAsync(Command command)
{
    var sandbox = await _aggregateStore.GetByIdAsync<Sandbox>(command.SandboxId);
    var response = await _azureService.CreateRedisInstance(sandbox.Id);
    if (response.IsSuccess)
    {
        sandbox.CreateRedisDetails(response);
        return sandbox;
    }

    sandbox.FailSetup(response.Errors.Select(e => e.Message));
    return sandbox;
}

这里的问题是沙箱聚合需要在调用外部服务之前处于正确的状态,我不能同时满足两者。我在这里唯一的想法是创建单独的方法CanCreateRedisInstance 检查聚合状态是否有效,然后才调用外部服务。我不喜欢的是我引入了验证方法

public async Task<IAggregateRoot> ExecuteAsync(Command command)
{
    var sandbox = await _aggregateStore.GetByIdAsync<Sandbox>(command.SandboxId);
    if(!sandbox.CanCreateRedisInstance())
    {
       throw new ValidationExcetpion("something");
    }

    var response = await _azureService.CreateRedisInstance(sandbox.Id);
    if (response.IsSuccess)
    {
        sandbox.CreateRedisDetails(response);
        return sandbox;
    }

    sandbox.FailSetup(response.Errors.Select(e => e.Message));
    return sandbox;
}

我想到的另一种方法是让整个过程更加cqrs-ish。

public async Task<IAggregateRoot> ExecuteAsync(Command command)
{
    var sandbox = await _aggregateStore.GetByIdAsync<Sandbox>(command.SandboxId);
    sandbox.ScheduleRedisInstanceCreation();
}

public void ScheduleRedisInstanceCreation()
{
  if(RedisInstanceDetails != null)
  {
     throw new ValidationException("something")
  }
  RedisInstanceDetails = RedisInstanceDetails.Scheduled(some arguments);
  AddEvent(new RedisInstanceCreationScheduled(some arguments));
}

RedisInstanceCreationScheduled 事件被发送到队列并由事件处理程序选择 这将调用外部服务并根据结果创建其他事件

public async Task ExecuteAsync(RedisInstanceCreationScheduled event)
{
    var sandbox = await _aggregateStore.GetByIdAsync<Sandbox>(command.SandboxId);
    var response = await _azureService.CreateRedisInstance(sandbox.Id);
    if (response.IsSuccess)
    {
        sandbox.CreateRedisDetails(response);
        return sandbox;
    }

    sandbox.FailSetup(response.Errors.Select(e => e.Message));
   _aggregateStore.Save(sandbox);
}

但是这种方法增加了一些额外的复杂性,我不太确定事件处理程序是否应该修改聚合。

【问题讨论】:

    标签: domain-driven-design cqrs


    【解决方案1】:

    这两种方法都可以。


    为什么没有验证应该留在处理程序中?当您更改域中的某些内容时,域对象也会对操作进行验证,如果不可能,则拒绝它。在这里你只需要与外部服务交互来验证它。

    外部服务只是领域层中的一个接口,您将使用一个具体的类在基础设施层中实现它。因此,您不会直接与 azure 绑定,而是一个服务,比如说 CloudService,它的实现使用了 Azure。这允许您构建由保留在基础架构层中的类引发的与域相关的异常。


    CQRS 方法也是有效的。但使用时要小心。

    例如,您可以启动一个 saga,请求外部服务创建实例 (CreateRedisInstance),然后根据您获得的事件(成功或失败)继续与下一个处理程序。但是您确实必须注意中间情况:应该如何处理两个操作之间的失败?如果第二个操作以失败告终,您还需要回滚第一个操作。


    这么说,如果真的不需要处理复杂的过程,我会选择第一个。此外,它看起来都与同一个域相关(不需要域内操作),因此没有真正需要通过一个应该正确处理每个成功/失败状态的 saga 来增加复杂性。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-05-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多