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

Fel*_*ter 3 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;
    }
Run Code Online (Sandbox Code Playgroud)

有什么想法吗?

Aar*_*ght 9

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

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

在这种情况下,消息系统(NServiceBus)底层,你可能做什么或应该有某种事务处理系统的这实际上是跟踪交易本身.如果将它们分组为某种类型的批次(即工作日),则批次必须具有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; }
}
Run Code Online (Sandbox Code Playgroud)

这对应于以下操作顺序:

  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事件并完成传奇.

当然,您不一定需要这些WhenDateTime字段,您可以很容易地使用布尔标志来指示哪些步骤已完成.但重要的是你的传奇是跟踪交易状态,而不是交易数据.

不要试图把一切都塞进一个传奇中.它并不意味着取代交易系统,将它们整合在一起只是一点点粘合剂.