ドキュメント指向を思考中

銀行トランザクションの話を書きました。せっかくがんばって書いたのに、Definitive Guide をみたらRecipeに書いてありましたorz。なので追加を書こうと思います。

あのドキュメントを使うと、

// map
function(doc){
  if(doc.doc_type == "振り込み"){
    emit(doc.from, (-1) * doc.amount);  // 振り込み元にとっては出金
    emit(doc.to,   doc.amount);         // 振り込み先にとっては入金
  }
}

で、口座残高が計算できることは明らかかと思います。振り込み用紙のデータベースへの追加は1つのアトミックなトランザクションになるので、上記の方法で銀行データベースが1つの場合は、ある時点においてAの出金とBの入金に不整合が発生することはありません。

一方で、Aの口座とBの口座が異なる銀行口座の場合は少し問題になりそうなので考えてみます。ちょっとまだ正しいのかどうかわかりませんが(つっこみ募集)、こんな感じでかんがえればいいのでしょうか。

Aの口座が存在する銀行をX銀行、BのそれをY銀行とします。

このとき、Aは振り込み用紙をXのDBにPUTするはずです。このとき、まずは、

{
  doc_type : "振り込み",
  from   : { 
    acount: "A",
    bank : "X"
  },
  to     : {
    account: "B",
    bank : "Y"
  },
  amount : 10
}

を登録します。from,toは明示的に銀行名を指定。で、次にX銀行は、X -> Y へのレプリケーションを行って送金を行います。

X は A の口座残高を計算するときは、XとYの両方を使って計算することになります。つまり、

  • SUM(X 銀行内で閉じた入出金処理 + Xに登録されたYからの振り込み用紙) + SUM(Yに登録されたXからの振り込み用紙)

で計算する。X銀行では、fromがXでtoがXではない振り込み用紙の計算はしない、というところがポイントです。Yに届いていない限り、振り込み送金処理はまだ行われていない、と解釈する。

トランザクションシステムになると、いつも決まったように2PhaseCommit厨が現れるのですが(しかもそういう人に限って2PhaseCommitを使っていない)、最初から2つ以上のDBを"同時に"更新する、などという難しいことは考えずに、ただしい最終状態(上記の場合、振り込み用紙がYに届いていること)になったら初めて結果に反映させましょうや、でいける場合もあるのです(というか、2PCにしても、本質はそこなんだし)。ま、単純にやってしまうと、スケールしない場合も多々ありますが。

これ、ちゃんとみれば、「異なる銀行への振り込み処理」が1つのワークフローで、たまたまレプリケーションという手段で実現されているだけなんですよね。ドキュメント指向を考えると、どうしてもワークフローシステムからは逃れられないのだと思います。そして、ワークフローシステムにおいて、"自動解決されない"競合と手動レプリケーションはいい効果を発揮してくれます。

その話はまたあとで、dWの最終回書き終わったのでそれが公開されてからにします。広島城を散歩してきます@6:30am。