0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

同期通信サーガでの大きな注意点

Last updated at Posted at 2025-10-09

前置き

今回は、以下の記事のリトライの内容をより深ぼった内容になります。

おとぎ話サーガや、タイムトラベルサーガでは、アプリケーションコードとして、
一時的にリトライ系の処理を実装する必要があります。

この影響によって、おとぎ話サーガの初期の段階では、
サービス同士が互いにアプリケーション層で、結合した状態にならざるを得ません。

下図のように、ビジネスロジック(クリーンアーキテクチャでいうエンティティ層)やユースケース層では独立。

image.png

今回の論点

なぜ最初からインフラリトライ系の処理をインフラコードとして表現しないのか?
疑問に感じませんか?

そこで今回の論点は、

なぜ、インフラ系の処理を最初からインフラ層に実装せずに、
図の緑部分のInterface Adapters層に実装する必要があるのか?

ということにします。

クリーンアーキテクチャにおけるサービス間通信

本題に入る前に、まずサービス量子内の構造について、整理しておく必要があります。

その際に、クリーンアーキテクチャの話が避けては通れません。

クリーンアーキテクチャの最も重要なルールは

「依存関係は常に外側から内側に向かう」

です。内側の円は、外側の円(具体的な実装)について何も知りません。

image.png

なので、下図のように、オーケストレーターのユースケース層(赤部分)が、
呼び出されるマイクロサービスのコントローラーに依存するようなコードはNG。

image.png

そして、オーケストレーターが各種マイクロサービスを呼び出す際の、正しいステップは以下のようになります。

1. オーケストレーターのユースケース層

役割

ビジネスロジックの実行を決定する。

アクション

自身の内側にあるユースケースが、「マイクロサービスAの機能が必要だ」と判断します。

しかし、ユースケースは外部通信の方法(それがHTTPなのかgRPCなのか)を知りません。
代わりに、自身が依存するインターフェース(抽象)を呼び出します。

例えば、i_microservice_a_gateway.execute_action(data)

2. オーケストレーターのゲートウェイ層 (Interface Adapters)

役割

外部システムとの通信という具体的な実装を担当する。

アクション

上のステップ1で呼び出されたインターフェースを実装した具象クラスがここに存在します。

このクラスが、HTTPクライアントを使ってマイクロサービスのAPIエンドポイントを呼び出すという、具体的なネットワーク通信処理とリトライ処理を実装します。

3. マイクロサービスのコントローラー層 (Interface Adapters)

役割

外部からのリクエストを受け付ける窓口。

アクション

オーケストレーターのゲートウェイから送られてきたHTTPリクエストを受け取ります。

リクエストの正当性を検証し、データを内部のユースケース層が理解できる形式に変換して、内側のユースケースを呼び出します。

4. マイクロサービスAのユースケース層

役割

自身のビジネスロジックを実行する。

アクション

コントローラーから渡されたデータをもとに、自身の責務であるビジネスロジックを実行します。
このユースケースは、自分が誰から(オーケストレーターから?それともユーザーから直接?)呼び出されたのかを知る必要はありません。

なぜこの分離が重要か?

この厳格な分離により、以下のメリットが生まれます。

強制ではないものの、正直個人的にはマイクロサービスするんなら、
最低限クリーンアーキテクチャの構造は、共通言語になっててよとは思います。
その理由は、以下を読んでもらえればすぐわかると思います。

ビジネスロジックの保護

オーケストレーターのコアなビジネスロジック(ユースケース層)は、ネットワークエラーやリトライといった技術的な複雑さから完全に隔離されます。

これにより、ビジネスロジックはシンプルでテストしやすくなります。

技術選択の自由

呼び出される側のマイクロサービスがREST APIからgRPCに変わっても、オーケストレーター側で修正が必要なのはゲートウェイ層だけです。

オーケストレーターのユースケース層のコードは一切変更する必要がありません。

したがって、結合するのはあくまでもシステムの「境界」を担うアダプター層同士であり、中核であるユースケース層は、それぞれのシステム内で保護されたままとなります。

2種類のリトライ:責務の分離

リトライには大きく分けて2種類あり、それぞれ責任を持つべきレイヤーが異なります。

1. インフラ層が担うべきリトライ infrastructural

対象

一過性のネットワークエラーなど、冪等(べきとう)に再実行しても安全な技術的な問題。

事例

HTTP 503 Service Unavailable、接続タイムアウト。

メカニズム

サービスメッシュ(Istioなど)やAPIゲートウェイが、リクエストの内容を解釈せず、機械的に再試行します。

アナロジー

郵便配達員が手紙を届けに行った際、相手が一時的に留守だったため、10分後にもう一度訪ねるようなもの。配達員は手紙の中身(ビジネスロジック)には関知しません。

ここで中身がもろ見えだと、セキュリティ上の観点で超危険です。
誰にでも、その秘匿情報が見えてしまっているのですから。

2. アプリケーション層が担うべきリトライ 📝

対象

ビジネス上の状態に依存する、より複雑なエラー。
インフラ層は、このリトライをすべきか、すべきでないかを判断できません。

事例

例①

「在庫の棚卸しのため、現在在庫がロックされています。5分後に再試行してください。」

例②

「決済プロバイダーのレート制限に達しました。指数関数的バックオフで再試行してください。」

例③

「依存サービスのデータ整合性が遅延しています。完了通知を待ってから再試行してください。」

メカニズム

アプリケーションコードがエラーの内容を解釈し、「今リトライしても無駄か?」
「どのくらい待ってからリトライすべきか?」といったビジネス判断を下します。

アナロジー

手紙の中身が「1万円を請求します」という内容だったとします。

相手が「今9千円しかないので、1時間後の給料日以降にまた来てください」と返答したとします。
この判断は、手紙の中身を知らない郵便配達員にはできず、請求者(アプリケーション)が次のアクションを決めるしかありません。

なぜ最初からインフラで実装しないのか?

では、なぜインフラ層の関心として最初から、リトライ系の処理として実装し、
おとぎ話サーガは、インフラ層でのみ他サービスと同期通信で蜜結合状態を目指せないのでしょうか?

1. 探索フェーズとしての「おとぎ話サーガ」

「おとぎ話サーガ」は、サービス境界を発見し、サービス間の真の依存関係と失敗パターンを学習するための探索的な段階です。

この時点では、

・どのようなエラーが起きるか

・それがインフラ層で機械的にリトライできるものなのか

・アプリケーション層での判断が必要なものなのか

がまだ明確ではありません。

アプリケーションコード内であれば、開発チームはこれらの複雑なリトライロジックを迅速に実装・実験・修正できます。

なぜなら、この段階で必要なリトライ処理には、

インフラ層が判断できない
『ビジネスロジック固有の条件』 が多く含まれているからです。

なので、まずはアプリケーション層でその複雑な要件を探索・検証する必要があります。

2. 時期尚早な共通化の回避 (YAGNI)

学習の不十分な段階で、最初から全てのパターンを網羅する完璧なリトライ機構をインフラ層に作ろうとすると、

過剰に複雑で、実際には使われない機能を作ってしまうリスクがあります。
(時期尚早な共通化)

そこで、まずは各アプリケーションチームが必要なリトライ処理を個別に実装し、その中で共通して使えるパターン(例:特定のエラーコードに対する指数関数的バックオフなど)が見えてきた段階で、初めてそれをライブラリやインフラ層の機能として抽出し、インフラ層に移動するのが、無駄のない進化の順序です。

つまり、一時的にアプリケーションコードにリトライ処理を実装するのは、未知の領域を探索するための、最も手早く安全な学習方法です。

つづき

この次の記事で、ストラングラーパターンを用いて、段階的にインフラ層に責務を委譲していく手法をフレームワーク化したものとして記述していきます。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?