はじめに
BFF(Backend For Frontend)で複数のマイクロサービスを束ねる構成を設計していると、「サービスAの更新は成功したのに、サービスBの更新で失敗した…データの整合性どうする?」という課題に必ずぶつかります。
分散システムでは、伝統的なRDBのようなACIDトランザクション(2相コミット等)を使うのが困難です。そこでよく採用されるのがSaga(サガ)パターンです。
実務の設計議論にて登場したこの手法について、基本概念からBFFでの実装イメージ、メリット・デメリットまでまとめました。
1. Sagaパターンとは?
Sagaパターンは、1つの大きな分散トランザクションを「複数のローカルトランザクションの連鎖」として分割して処理するデザインパターンです。
最大の特徴は、途中の処理でエラーが発生した場合に「補償トランザクション(Compensating Transaction)」と呼ばれる打ち消し処理を逆順に実行し、システム全体を元の正常な状態に戻す(最終的整合性を保つ)点です。
[正常系]
処理Aを実行 ──> 処理Bを実行 ──> 処理Cを実行 (完了)
[異常系(処理Cで失敗した場合)]
処理Aを実行 ──> 処理Bを実行 ──> 処理Cで失敗!
│
▼
補償処理A ◄── 補償処理B (元の状態にロールバック)
2. 2つのアプローチ(オーケストレーション vs コレオグラフィ)
Sagaパターンには大きく分けて2つの実現方法があります。BFFを挟む構成では「オーケストレーション型」が採用されることが一般的です。
① オーケストレーション型(BFF主導)
BFFなどの中央コンポーネントが「司令塔(オーケストレーター)」となり、各サービスへリクエストを順番に送ります。失敗を検知したら、BFFが自ら補償トランザクションを呼び出します。
メリット: 処理フローがBFF側に集約されるため、全体像の把握やデバッグがしやすい。
デメリット: BFF側のロジックが肥大化しやすい(Smart UI/Fat Agent化に注意)。
② コレオグラフィ型(イベント駆動)
中央の司令塔を置かず、各サービスがドメインイベントを発行・購読(Publish/Subscribe)して自律的に連鎖処理を行います。
メリット: サービス間の結合度が低く、自律性が高い。
デメリット: 処理の流れを追うのが難しく、デバッグや監視の難易度が上がる。
3. 具体例:ECサイトの注文処理
「注文サービス」「在庫サービス」「決済サービス」の3つをBFFでまとめるケースを考えます。
正常フロー(オーケストレーション型)
BFF ──> 注文サービス: 注文データ作成 (仮)
BFF ──> 在庫サービス: 在庫の引き当て
BFF ──> 決済サービス: クレジットカード決済
BFF ──> 注文サービス: 注文確定
異常フロー(決済で失敗した場合)
BFF ──> 注文サービス: 注文データ作成 (仮) [成功]
BFF ──> 在庫サービス: 在庫の引き当て [成功]
BFF ──> 決済サービス: 決済処理 [失敗!]
BFF ──> 在庫サービス: 在庫引き当ての解除 [補償トランザクション]
BFF ──> 注文サービス: 注文のキャンセル [補償トランザクション]
4. 設計時にハマりやすい注意点・デメリット
設計案として提出・議論する際は、以下のデメリットや注意点も把握しておく必要があります。
補償処理が失敗した場合の考慮
「補償トランザクション自体がネットワークエラーなどで失敗したらどうするか?」という問題があります。リトライ機構や、最終的に手動運用でリカバリするためのログ/アラート設計が不可欠です。
中間状態(Dirty Read)が見えてしまう
ACIDの「I(Isolation / 隔離性)」がないため、処理の途中の状態が他ユーザーから見えてしまう可能性があります。「在庫が減ったが、決済失敗で戻った」といったチラつきを許容できる仕様設計が必要です。
BFFの責務過多
BFFに複雑なSagaの状態管理ロジックを持ち込みすぎると、BFFの本来の役割(画面用のデータ整形・集約)を超えてビジネスロジックで肥大化してしまいます。状態管理が複雑化する場合は、TemporalやAWS Step Functionsなどのワークフローエンジン導入を検討するのが無難です。
5. まとめ
Sagaパターンは、分散システムで補償処理を使って「最終的整合性」を保つ手法。
BFF構成では、BFFが司令塔になるオーケストレーション型がよく使われる。
補償処理の失敗対策や、BFFが肥大化しないような責務分離の設計が成功のカギ。
分散システムの整合性設計で悩んだ際の選択肢として、ぜひ検討してみてください!