DEV Community 日本語 フォロー Symfony Messenger Transports: Transactional Outboxを上に構築する デュアルライト問題は、アプリケーションがデータベースレコードにデータを書き込み、その後メッセージブローカーにイベントを発行し、これらの操作のいずれかが失敗した場合に発生します。これにより、データベースに注文が存在するものの、下流システムに通知されない、あるいはその逆の状態が生じ、ゴーストオーダーやチャージバックのような問題を引き起こす可能性があります。Symfony Messengerによって実現されるトランザクショナルアウトボックパターンは、堅牢なソリューションを提供します。その中心的な考え方は、プライマリデータ書き込みと同じトランザクション内で、イベントをデータベーステーブル(アウトボック)に挿入することです。これにより、アトミック性が保証されます。つまり、注文とイベントレコードは一緒にコミットされるか、全くコミットされないかのどちらかになります。Symfony MessengerのDoctrineトランスポートは、このアウトボックテーブルとして機能するように設定できます。イベントをDoctrineトランスポートにルーティングすることで、イベントメッセージのINSERT操作は既存のデータベーストランザクションに含まれます。トランザクションがコミットされた後、通常messenger:consumeという別のプロセスがリレーとして機能します。このリレーはアウトボックテーブルからメッセージを読み取り、RabbitMQなどの意図された宛先にディスパッチします。これにより、イベントディスパッチと初期リクエスト処理が分離され、ブローカーの障害や一時的な再起動に対するシステムの耐性が向上します。messenger:consumeコマンドは、複数のワーカーが重複なくアウトボックからメッセージを同時に処理できるように、SELECT FOR UPDATE SKIP LOCKEDを使用します。重要なのは、アウトボックパターンは少なくとも1回の配信を保証するため、ハンドラーは潜在的なメッセージの再再生を安全に処理できるように冪等である必要があるということです。これは、安定したイベントIDを保存し、副作用を実行する前に処理済みイベントテーブルを確認することで達成できます。イベントをDoctrineトランスポートにルーティングし、messenger:consumeをリレーとして使用することで、アプリケーションはカスタムインフラストラクチャを必要とせずに信頼性の高いイベント配信を実現できます。このアプローチは、インフラストラクチャの懸念をエッジに保ち、ドメインがビジネスロジックに集中できるようにし、将来的なメッセージトランスポートの変更を容易にします。 Symfony Messenger Transports: Building a Transactional Outbox on Top dev.to DEV Community 日本語 RSS thenote.app
INSERT操作は既存のデータベーストランザクションに含まれます。トランザクションがコミットされた後、通常messenger:consumeという別のプロセスがリレーとして機能します。このリレーはアウトボックテーブルからメッセージを読み取り、RabbitMQなどの意図された宛先にディスパッチします。これにより、イベントディスパッチと初期リクエスト処理が分離され、ブローカーの障害や一時的な再起動に対するシステムの耐性が向上します。messenger:consumeコマンドは、複数のワーカーが重複なくアウトボックからメッセージを同時に処理できるように、SELECT FOR UPDATE SKIP LOCKEDを使用します。重要なのは、アウトボックパターンは少なくとも1回の配信を保証するため、ハンドラーは潜在的なメッセージの再再生を安全に処理できるように冪等である必要があるということです。これは、安定したイベントIDを保存し、副作用を実行する前に処理済みイベントテーブルを確認することで達成できます。イベントをDoctrineトランスポートにルーティングし、messenger:consumeをリレーとして使用することで、アプリケーションはカスタムインフラストラクチャを必要とせずに信頼性の高いイベント配信を実現できます。このアプローチは、インフラストラクチャの懸念をエッジに保ち、ドメインがビジネスロジックに集中できるようにし、将来的なメッセージトランスポートの変更を容易にします。