【问题标题】:What are sagas and why are they used event driven architectures?什么是 sagas,为什么它们使用事件驱动架构?
【发布时间】:2023-12-13 11:56:01
【问题描述】:

我遇到了以下代码:

  public class ShippingSaga : Saga<ShippingSagaData>,
         ISagaStartedBy<OrderAccepted>,
         ISagaStartedBy<CustomerBilledForOrder>
     {
        public void Handle(CustomerBilledForOrder message)
         {
             this.Data.CustomerHasBeenBilled = true;
             this.Data.CustomerId = message.CustomerId;
             this.Data.OrderId = message.OrderId;

             this.CompleteIfPossible();

        }

        public void Handle(OrderAccepted message)
        {
            this.Data.ProductIdsInOrder = message.ProductIdsInOrder;
            this.Data.CustomerId = message.CustomerId;
            this.Data.OrderId = message.OrderId;

            this.CompleteIfPossible();
        }

        private void CompleteIfPossible()
        {
            if (this.Data.ProductIdsInOrder != null && this.Data.CustomerHasBeenBilled)
            {
                this.Bus.Send<ShipOrderToCustomer>(
                    (m =>
                    {
                        m.CustomerId = this.Data.CustomerId;
                        m.OrderId = this.Data.OrderId;
                        m.ProductIdsInOrder = this.Data.ProductIdsInOrder;
                    }
                    ));

                this.MarkAsComplete();
            }
      }
}

从上面的代码中看,sagas 似乎是某种更高级别的事件协调器/控制器。这是真的吗?如果是这样,它们是否仅用于事件驱动架构?最后,传奇是基础设施的一部分吗?

第一个问题似乎得到了回答。但就责任(即基础设施)而言,它们真正属于哪里?领域 ? .这些是否仅适用于 EDA?

【问题讨论】:

  • 您所描述的是一个 ProcessManager,一个 Saga(根据其原始定义)由多个 ProcessManager 和每个步骤的补偿动作描述。但大多数时候,人们使用 Saga 来描述 ProcessManager。

标签: domain-driven-design nservicebus-sagas eda


【解决方案1】:

警告:存在一些混淆,尤其是在 对“Saga”的定义;见下文。

流程管理器从根本上说是读取模型——您从事件历史记录中恢复它们,并查询它们以获取应运行的命令列表。

它们类似于人类查看视图并向写入模型发送命令。有关此观点的更多信息,请参阅 Rinat Abdullin 的文章 Evolving Business Processes

它们用作业务流程的描述,也就是说,它们标识了应该由聚合运行的其他决策(命令)。在实现中,它们非常像状态机 - 给定事件 X 和事件 Y,流程管理器处于状态 (XY),它会推荐的命令是固定的。

如果您将状态机(纯逻辑)与副作用(与总线的交互)区分开来,我发现它们更容易思考。

public class ShippingSaga : Saga,
     ISagaStartedBy<OrderAccepted>,
     ISagaStartedBy<CustomerBilledForOrder>
{
    public void Handle(CustomerBilledForOrder message)
    {
        this.process.apply(message);
        this.CompleteIfPossible();
    }

    public void Handle(OrderAccepted message)
    {
        this.process.apply(message);
        this.CompleteIfPossible();
    }

    private void CompleteIfPossible()
    {
        this.process.pendingCommands().each ( m=>
            this.Bus.Send(m);
        }
    }
}

或者等价的——如果你更喜欢考虑不可变的数据结构

public class ShippingSaga : Saga,
     ISagaStartedBy<OrderAccepted>,
     ISagaStartedBy<CustomerBilledForOrder>
{
    public void Handle(CustomerBilledForOrder message)
    {
        this.process = this.process.apply(message);
        this.CompleteIfPossible();
    }

    public void Handle(OrderAccepted message)
    {
        this.process = this.process.apply(message);
        this.CompleteIfPossible();
    }

    private void CompleteIfPossible()
    {
        this.process.pendingCommands().each ( m=>
            this.Bus.Send(m);
        }
    }
}

所以运输流程是根据业务领域定义的,NServiceBus“Saga”将该业务领域与总线基础设施接口。关注点分离不是很好吗?

我在引号中使用“Saga”是因为 -- NService bus sagas 不是特别适合 prior use of the term

术语 saga 在 CQRS 的讨论中常用来指代在有界上下文和聚合之间协调和路由消息的一段代码。但是,出于本指南的目的,我们更喜欢使用术语流程管理器来指代这种类型的代码工件。这有两个原因:

  • saga 一词有一个众所周知的预先存在的定义,其含义与通常理解的与 CQRS 相关的含义不同。
  • 术语流程管理器更好地描述了此类代码工件所执行的角色。

【讨论】:

    最近更新 更多