C# 我的传奇结构是正确的解决方案吗?(N服务总线)

C# 我的传奇结构是正确的解决方案吗?(N服务总线),c#,nhibernate,nservicebus,saga,C#,Nhibernate,Nservicebus,Saga,我直接切入。我正试图通过发送到我们银行的文件来实现信用卡支付的自动化。银行不会实时验证卡付款。银行隔夜处理付款,并在第二天发送一份包含成功和失败付款的响应文件 我有一个web应用程序,当它接受或取消付款时,一条包含付款/取消详细信息的消息(通过Bus.Send)发送到命令消息处理器 然后,处理器发布(通过Bus.Publish)此消息,以供所有服务查看 一项服务需要执行以下操作: 从收到第一条消息开始一段传奇故事 发出超时请求以关闭业务 跟踪所有后续消息(在saga中) 收到超时后,将付款和取

我直接切入。我正试图通过发送到我们银行的文件来实现信用卡支付的自动化。银行不会实时验证卡付款。银行隔夜处理付款,并在第二天发送一份包含成功和失败付款的响应文件

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

然后,处理器发布(通过Bus.Publish)此消息,以供所有服务查看

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

  • 从收到第一条消息开始一段传奇故事
  • 发出超时请求以关闭业务
  • 跟踪所有后续消息(在saga中)
  • 收到超时后,将付款和取消消息转换为银行文件
问题是,我不知道如何在传奇中存储消息集合(或其他相关内容),因为列表不允许作为虚拟成员

以下是当前的传奇结构:

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;
    }
公共类PaymentRequestCancelledSagaBase:IContainSagaData
{
//以下属性是必需的
公共虚拟Guid Id{get;set;}
公共虚拟字符串发起者{get;set;}
公共虚拟字符串OriginalMessageId{get;set;}
//收到的所有PaymentRequestedMessages的列表
公共虚拟列表PaymentRequestedMessages;
//收到的所有PaymentCanceledMessages的列表
公共虚拟列表付费取消消息;
}

有什么想法吗?

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

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

在这种情况下,在消息传递系统(NServiceBus)的底层,您可能会或应该有某种事务系统,它实际上跟踪事务本身。如果他们被分组到某一类型的批次(即工作日),那么批次必须有一个ID。除了关于整个流程状态的基本信息(即,您收到银行的回复了吗?)之外,这就是传奇应该引用的ID

服务总线本身并不是一个系统,它是独立系统和组件相互通信的一种方式。事实上,传说一旦完成(通过
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; }
}
对应于以下操作顺序:

  • 请求发送到应用程序,应用程序创建/追加批处理并发布
    PaymentRequested
    事件
  • 订阅者选择“PaymentRequested事件”,如有必要,创建一个新的saga并设置
    RequestBatchId
    ,该ID将成为saga关联ID。然后设置业务关闭的超时时间
  • 超时处理程序关闭批,设置
    WhenRequestBatchClosed
    ,并发布
    PaymentRequestBatchClosed
    事件
  • 订阅者拾取
    PaymentRequestBatchClosed
    事件,创建付款文件(设置
    BankRequestFileName
    ),发布一个
    RequestFileAvailable
    事件,表示文件已准备就绪
  • 订阅者拾取
    RequestFileAvailable
    事件并启动上载过程,该过程(例如)将文件FTPs到银行服务器,在请求文件发送时更新
    ,并发布
    RequestFileSent
    事件
  • 监视进程(或另一个超时处理程序)检测响应文件,在收到响应文件时更新
    BankResponseFileName
    ,并发布
    ResponseFileAvailable
    事件
  • 订阅者选择
    ResponseFileAvailable
    事件,处理文件,创建付款批次,更新
    PaymentBatchId
    ,并可选择发布最终的
    PaymentsProcessed
    事件并完成saga
  • 当然,当
    DateTime字段时,您不一定需要这些
    ,您可以很容易地使用布尔标志指示哪些步骤已完成。但重要的是,您的传奇是跟踪事务状态,而不是事务数据。


    不要错误地试图把一切都塞进一个传奇故事。它并不是要取代事务系统,它只是一点胶水,可以将所有系统粘合在一起。

    IList受支持,请参见制造示例。我会将支付ID列表保存在该列表中,以便银行文件处理程序使用这些支付ID查找交易金额,而不是在消息中传输这些信息?在这种情况下,我需要向processor添加一个数据访问层,以获取消息中没有的数据。您也可以存储该信息,就像生产示例中使用的订单行(DTO)一样。web应用程序不知道可能添加了多少付款,也不知道可能没有添加多少付款,因此不会生成批处理。在收到超时消息并整理消息之前,不会生成批处理。处理器生成的唯一标识符实际上是批次ID。这包括在银行文件和发送文件后发送的BankFileSent消息中。另一个处理器使用BankFileSent并更新付款记录。当响应返回时,我们有唯一标识符和最终更新的付款ID。@Fellmeister:我很抱歉