Saga的设计

14-09-12 brighthas
              

下面是 saga的代码,用JS实现的,省略了多余代码。希望banq大哥和各位指点。

// 定义Saga的一个例子

var T = Saga.extend({

    // 启动方法
    startup: function () {
           // 减少用户1 的钱
           user1.减少钱() ; //产生领域事件 “User - ”
    }

    // process manage 监听领域事件,根据这些事件发起新事件。
    handles: {
        // 用户1 减少钱事件
        "User - ": function (event) {
              // 用户1减少钱了,把这笔钱给用户2
             user2.增加钱();  // 会产生领域事件 “User +”
        },
        // 监听到 用户2 增加钱事件
        "User + ": function (event) {
               // 这时候转账结束,要publish一个 completed事件,表示saga结束。
               this.publish("completed");
        }
    }

})

<p>

saga也是一个聚合根,拥有自己的uid,在command handle里可以直接像聚合一样调用。下面是 command handle 的调用代码,省略了多余代码,看起来更清晰。

var newSaga = sagaRepository.create(...);
newSaga.startup();
<p>

saga公开的只有一个startup方法。

              

tangxuehua
2014-09-12 12:36

也可以看看我用c#实现的基于saga思想的转账的例子:

https://github.com/tangxuehua/enode/tree/master/src/Samples/BankTransferSample

brighthas
2014-09-12 13:16

谢谢,那你看看我这个思路有什么问题没有?

liangshan
2014-09-12 16:39

我想了一下转账这个问题,能不能这样考虑:

将转账这个事务看作一次运动,这是一次代表钱的数字从user1运动到user2的运动。我们可以将这次运动用如下形式标识:

public class MoneyTransfer {

public Guid Id {get; private set;}

public Guid FromAccountID {get; private set;}

public Guid ToAccountID {get; private set;}

public decimal MoneyValue {get; private set;}

}

然后这个运动的完成过程由于被我们分成了两个并行的步骤——FromAccountID的减钱和ToAccountID账户的加钱。于是MoneyTransfer中不得不加入跟踪这两个步骤完成情况的状态字段,变成下面这个样子:

public class MoneyTransferActor {

private Guid Id {get; set;}

private Guid FromAccountID {get; set;}

private Guid ToAccountID {get; set;}

private decimal MoneyValue {get; set;}

private bool[] dones{get;set;}

private IActor[] ChildActors{get;set;}

}

MoneyTransferActor中有两个ChildActor,一个负责user1的减钱一个负责user2的加钱。MoneyTransferActor负责监管整个转账事务,它的两个子actor必须都成功,如果其中一个成功了而另一个失败了则MoneyTransferActor负责通知成功的那个子actor回退。

事务完成后MoneyTransferActor的状态持久化起来即是完整的日志,完整的可以原路返回。

banq
2014-09-13 13:42

2014-09-12 16:39 "@anycmd"的内容
MoneyTransferActor负责监管整个转账事务 ...

是的,需要一个监管全局事务的管理者,但不能使用锁。Akka的Actor模型使用的是语言本身协调机制:

try {
coordinated atomic {
    account1 ! coordinated.coordinate(Deposit(75))
    account2 ! coordinated.coordinate(Withdraw(75))
}
} catch {
    case ex:Exception => println(ex.getMessage)
}
<p>