我的Saga结构是正确的解决方案吗? (NServiceBus)

时间:2011-10-26 04:17:43

标签: c# nhibernate nservicebus saga

我会直截了当地说。我正在尝试通过发送到我们银行的文件自动化信用卡付款。卡付款未经银行实时验证。银行隔夜处理付款,并在第二天发送回复文件,同时付款成功和不成功。

我有一个网络应用程序,当它接受或取消付款时,会将包含付款/取消详细信息的消息(通过Bus.Send)发送到命令消息处理器。

然后处理器(通过Bus.Publish)发布此内容供所有服务查看。

一项服务需要执行以下操作:

  • 收到第一条消息后开始传奇
  • 发出关闭业务的超时请求
  • 跟踪所有后续消息(在传奇中)
  • 收到超时后,将付款和取消信息转入银行档案。

问题是我不知道如何在传奇中存储消息集(或其他任何内容),因为List<>'不允许作为虚拟成员。

这是当前的传奇结构:

public class PaymentRequestCancelledSagaBase : IContainSagaData
    {
        // the following properties are mandatory
        public virtual Guid Id { get; set; }
        public virtual string Originator { get; set; }
        public virtual string OriginalMessageId { get; set; }

        // List of all the received PaymentRequestedMessages
        public virtual List<PaymentRequested> PaymentRequestedMessages;

        // List of all the received PaymentCancelledMessages
        public virtual List<PaymentCancelled> PaymentCancelledMessages;
    }

有什么想法吗?

1 个答案:

答案 0 :(得分:9)

我不确定我的想法是否与Udi等人一致,但我总是将saga数据理解为非常轻量级的元数据关于消息,它应该不包含太多实际内容消息数据。

可能为每个付款请求和相应的批准/取消都有一个传奇,但我们假设银行要求您在每个工作日将它们全部批量处理,或者每个文件向您收取固定金额,这很常见......

在这种情况下,你可能做的消息系统(NServiceBus)或者 应该有某种事务系统,实际上跟踪 事务本身< / em>的。如果将它们分组到某种类型的批次(即工作日),则批次必须具有ID。除了有关整个过程状态的基本信息之外,那个是传奇应该引用的内容(即你是否从银行获得了回复?)。

服务总线本身不是一个系统,它是独立系统和组件相互通信的一种方式。实际上,Sagas一旦完成(通过MarkAsComplete)就会被删除,因此它们实际上不是存储“永久”信息的地方。

在我的世界中,传奇数据看起来就像这样:

public class PaymentProcessingSagaData : IContainSagaData
{
    public virtual Guid Id { get; set; }
    public virtual string Originator { get; set; }
    public virtual string OriginalMessageId { get; set; }

    public virtual int RequestBatchId { get; set; }
    public virtual DateTime? WhenRequestBatchClosed { get; set; }
    public virtual string BankRequestFileName { get; set; }
    public virtual DateTime? WhenRequestFileSent { get; set; }
    public virtual string BankResponseFileName { get; set; }
    public virtual DateTime? WhenResponseFileReceived { get; set; }
    public virtual int PaymentBatchId { get; set; }
}

这对应于以下操作的顺序:

  1. 发送给应用的请求,该应用创建/附加批量并发布PaymentRequested事件。
  2. 订阅者选择PaymentRequested事件,并在必要时创建新的传奇并设置RequestBatchId,这将成为传奇相关ID。然后它设置关闭业务的超时。
  3. 超时处理程序关闭批处理,设置WhenRequestBatchClosed并发布PaymentRequestBatchClosed事件。
  4. 订阅者选择PaymentRequestBatchClosed事件,创建付款文件(设置BankRequestFileName),发布RequestFileAvailable事件,表明该文件已准备就绪。
  5. 订阅者选择RequestFileAvailable事件并启动上传过程(例如)将文件FTP到银行服务器,更新WhenRequestFileSent并发布RequestFileSent事件。
  6. 监控进程(或其他超时处理程序)检测响应文件,更新BankResponseFileNameWhenResponseFileReceived并发布ResponseFileAvailable事件。
  7. 订阅者选择ResponseFileAvailable事件,处理文件,创建付款批,更新PaymentBatchId,并可选择发布最终PaymentsProcessed事件并完成传奇。
  8. 当然,您不一定需要这些When DateTime字段,您可以轻松地使用布尔标志来指示哪些步骤已完成。但重要的是您的传奇跟踪事务,而不是事务数据

    不要误以为要把所有东西都塞进一个传奇故事中。它并不意味着取代交易系统,将它们放在一起只是一些粘合剂。