オンプレのRDBMSで長年システムを作ってきたエンジニアが、マイクロサービスへの移行を進める中でよく壁にぶつかるのが「サービス間のデータ整合性」と「障害後の状態復元」の問題です。イベントソーシングは、そのどちらにも有効なアーキテクチャパターンですが、「どうやってAWSで実装するのか」「本当に自分たちのシステムに合うのか」という点で迷う現場エンジニアが多いと感じます。
この記事では、イベントソーシングパターンの基本概念をオンプレのDB設計と比較しながら解説し、AWS DynamoDB・Amazon Kinesis・AWS EventBridgeを使った実装パターンと、導入時のハマりポイントまで網羅します。
なぜイベントソーシングが必要なのか?(従来のDB設計との違い)
オンプレのシステムでは、データベースに「現在の状態」を保存するのが当たり前でした。たとえば注文テーブルに「注文ステータス: 発送済み」と書き込んだ瞬間、その前の「処理中」や「保留」という履歴はなくなります。
| 比較軸 | 従来のステート保存(オンプレ主流) | イベントソーシング |
|---|---|---|
| 保存するもの | 最新の状態(現在値) | 状態変化の原因となったイベント(履歴) |
| データの更新 | UPDATE/DELETEで上書き | イベントをAPPEND(追記)するのみ |
| 過去の状態の参照 | 別途監査ログが必要 | イベントをリプレイすれば任意時点の状態を再現できる |
| 障害後の復元 | バックアップ時点まで戻るしかない | イベントログから任意時点に復元可能 |
| 複数サービスへの変更通知 | ポーリングか変更検知(複雑) | イベントをサブスクライブするだけで自然に連携できる |
マイクロサービスでは、サービスAの更新をサービスBに伝えるために、DBを直接見に行くのではなく「何かが起きた」というイベントを共有するのが基本です。イベントソーシングはそのイベントをデータの中心に据えた設計思想です。
イベントソーシングの基本概念を整理する
1. イベントとは何か
イベントとは「過去に起きた事実」を表す不変のレコードです。たとえばECサイトの注文システムであれば:
・OrderPlaced: 注文が確定した
・PaymentProcessed: 支払いが処理された
・OrderShipped: 商品が発送された
・OrderCancelled: 注文がキャンセルされた
これらのイベントを時系列に並べたのが「イベントストア」です。現在の注文ステータスを知りたい場合は、関連するイベントをすべて読み込んで「再生(リプレイ)」することで状態を導き出します。
2. アグリゲートとイベントストア
イベントソーシングでは、「アグリゲート」という単位でイベントを管理します。注文であれば注文ID単位、ユーザーであればユーザーID単位です。
# イベントストアのデータイメージ(DynamoDBの場合) # aggregate_id(PK)+ version(SK)でイベントを一意に管理 aggregate_id: "order-12345" version: 1 event_type: "OrderPlaced" payload: {"customer_id": "c-001", "items": [...], "total": 5000} timestamp: "2026-07-01T10:00:00Z" aggregate_id: "order-12345" version: 2 event_type: "PaymentProcessed" payload: {"amount": 5000, "method": "credit_card"} timestamp: "2026-07-01T10:01:30Z" aggregate_id: "order-12345" version: 3 event_type: "OrderShipped" payload: {"tracking_number": "JP123456789", "carrier": "Yamato"} timestamp: "2026-07-01T14:30:00Z"
3. スナップショットで読み込みを最適化する
イベントが100件・1000件と積み上がると、毎回全件リプレイするのはコストがかかります。そのため定期的に「現在の状態を丸ごと保存したスナップショット」を取り、それ以降のイベントだけを追加でリプレイする最適化がよく使われます。
AWSでイベントソーシングを実装する主要パターン
1. Amazon DynamoDB をイベントストアとして使う
最もシンプルな構成は Amazon DynamoDB をイベントストアとして使う方法です。
・テーブル設計: aggregate_id(パーティションキー)+ version(ソートキー)の複合キー
・書き込み: バージョン番号の衝突チェックに条件付き書き込み(ConditionExpression: attribute_not_exists(version))を使い、楽観的ロックを実現する
・読み込み: Query APIでaggregate_idを指定してversion昇順に全件取得し、アプリ側でリプレイ
# AWS CLI — DynamoDBへのイベント書き込み(楽観的ロック付き) aws dynamodb put-item \ --table-name event-store \ --item '{ "aggregate_id": {"S": "order-12345"}, "version": {"N": "4"}, "event_type": {"S": "OrderShipped"}, "payload": {"S": "{\"tracking_number\":\"JP123456789\"}"}, "timestamp": {"S": "2026-07-01T14:30:00Z"} }' \ --condition-expression "attribute_not_exists(version)"
2. DynamoDB Streams + AWS Lambda で下流サービスに配信する
DynamoDB Streamsを有効にしておくと、イベントストアへの書き込みをトリガーにAWS Lambdaを起動し、他のマイクロサービスへ通知を飛ばせます。
・DynamoDB Streams: テーブルの変更を24時間保持し、Lambdaへ順序保証付きで配信
・AWS Lambda: 受け取ったイベントを解析し、Amazon SNS・Amazon SQS・EventBridgeへルーティング
・トランザクショナルアウトボックス不要: DB書き込みと通知を同一トランザクション内で保証する「トランザクショナルアウトボックスパターン」が不要になるのが大きなメリット
3. Amazon EventBridge でイベントバスを構築する
複数のマイクロサービスがイベントをやりとりする場合は、Amazon EventBridgeのカスタムイベントバスが有効です。
# AWS CLI — EventBridgeにイベントを送信する aws events put-events \ --entries '[ { "Source": "com.example.order-service", "DetailType": "OrderShipped", "Detail": "{\"aggregate_id\":\"order-12345\",\"version\":3,\"tracking_number\":\"JP123456789\"}", "EventBusName": "order-events-bus" } ]'
EventBridgeのルールで「DetailType = OrderShipped のイベントを配送通知サービスに転送する」という設定を書くだけで、サービス間の依存を最小化できます。
4. Amazon Kinesis Data Streams で大量イベントをさばく
IoTデータや金融トランザクションなど、毎秒数千件以上のイベントが流れる場合はAmazon Kinesis Data Streamsが向いています。
・シャード単位のスループット: 1シャードあたり書き込み1MB/秒・1,000レコード/秒、読み取り2MB/秒
・保持期間: デフォルト24時間(最大365日、延長はコスト増)
・コンシューマー: AWS Lambda・Amazon Kinesis Data Firehose・独自アプリから並列消費可能
料金の仕組み(コスト感覚を持つ)
イベントソーシングはイベントを追記し続けるため、データ量が単調増加します。コスト見積もりは早めに設計段階で行うことを強くすすめます。
| サービス | 課金単位 | 目安(2026年6月時点・東京リージョン ap-northeast-1) |
|---|---|---|
| DynamoDB(オンデマンド) | 書き込みリクエスト / 読み取りリクエスト / ストレージ | 書き込み$1.4285/100万WRU、読み取り$0.285/100万RRU |
| DynamoDB Streams | 読み取りリクエスト | $0.02/100万件(最初の250万件/月は無料) |
| Amazon EventBridge | イベント数 | $1.00/100万イベント(カスタムバス) |
| Amazon Kinesis Data Streams | シャード時間 + PUTレコード | $0.0165/シャード時間、$0.0165/100万PUTレコード |
【重要】スナップショット戦略でストレージコストを抑える
イベントを無限に積み上げるとDynamoDBのストレージコストが膨らみます。本番環境では以下を必ず検討してください。
・スナップショット間隔: 100イベントごと、または1日1回など定期的にスナップショットを取得
・古いイベントのアーカイブ: DynamoDB TTLで古いイベントを削除し、Amazon S3(Glacier)にエクスポート
・コールドストレージコスト: Amazon S3 Glacierは$0.004/GB/月(2026年6月時点)と非常に安価
応用・実務Tips
【重要】CQRSとの組み合わせが実践の基本形
イベントソーシングは単体では「書き込みは楽だが読み込みが重い」という課題があります。実務ではCQRS(コマンドクエリ責任分離)パターンと組み合わせるのが定石です。
・書き込み側(Command): イベントをイベントストアに追記するのみ
・読み込み側(Query): イベントを処理した読み取り専用のビュー(Read Model)をDynamoDB・Amazon Redshift・Amazon OpenSearch Service等に別途作成
・最終的整合性: 書き込みとRead Modelの同期に数十ミリ秒~数秒の遅延が生じる点を設計で許容すること
姉妹記事「CQRS(コマンドクエリ責任分離)パターン入門」も合わせて参照してください。
イベントのスキーマ進化(バージョニング)
イベントストアに蓄積した過去のイベントは変更できません。将来的にイベントのフォーマットが変わった場合の対応が実務で必ず問題になります。
・アップキャスター: 古いバージョンのイベントを読み込み時に新バージョンに変換する変換関数を用意する
・イベントバージョン番号: イベントペイロードに schema_version フィールドを最初から含めておく
・新旧スキーマの並行運用: 移行期間中は新旧両フォーマットのイベントをリプレイできるようにする
Amazon EventBridgeのスキーマレジストリを活用すると、スキーマのバージョン管理と変更通知を仕組みとして提供できます。
べき等性(Idempotency)の担保
ネットワーク障害でイベントが重複配信された場合でも、下流サービスが同じイベントを2回処理しても結果が変わらない「べき等性」を保証する設計が必要です。
・イベントIDのチェック: 各イベントに一意のIDを付与し、処理済みIDをDBで管理して重複をスキップ
・DynamoDBの条件付き書き込み: 同じ集約ID+バージョン番号への重複書き込みは自動的に失敗する仕組みを活用
よくあるトラブルと対処法
トラブル1: DynamoDBのホットパーティション問題
特定のaggregate_idへのアクセスが集中すると、そのパーティションがホットスポットになりスループット上限に達することがあります。
対処法: aggregate_idのプレフィックスをUUID形式にしてアクセスを分散する。スナップショットを活用して読み取りコストを下げる。アクセスパターンが予測できる場合はプロビジョニングドキャパシティ+Auto Scalingに切り替える。
トラブル2: イベントのリプレイが遅くなる
イベントが数万件に達すると、起動のたびに全件リプレイするためレイテンシが問題になります。
対処法: スナップショットの取得頻度を上げる(50イベントごとなど)。Amazon DynamoDB Accelerator(DAX)を導入してスナップショットのキャッシュ読み取りを高速化する。
トラブル3: イベント配信の順序が保証されない
Amazon EventBridgeやAmazon SNSはAt-Least-Onceの配信であり、順序は保証されません。
対処法: 各イベントにversion番号を付与し、下流サービスで順序チェックを行う。順序保証が必須の場合はAmazon Kinesis Data Streams(シャードキー一致で順序保証)を選択する。
トラブル4: イベントスキーマ変更の周知漏れ
イベントスキーマを変更したときに全コンシューマーが対応できるまで旧スキーマを維持する必要がある一方、移行完了したサービスを個別に把握しづらいという問題が起きがちです。
対処法: Amazon EventBridgeのスキーマレジストリを活用してスキーマをバージョン管理する。コンシューマーごとの対応完了を追跡するスプレッドシートと移行計画を作成し、旧スキーマの廃止時期を事前に告知する。
本記事のまとめ
| ポイント | 内容 |
|---|---|
| イベントソーシングとは | 「現在の状態」ではなく「状態変化を引き起こしたイベント」を追記で保存するパターン |
| メリット | 過去の任意時点への状態復元・監査ログの自然な実現・マイクロサービス間連携のシンプル化 |
| AWSでの主な実装 | Amazon DynamoDB(イベントストア)+ DynamoDB Streams または EventBridge(配信)+ AWS Lambda(処理) |
| CQRSとの組み合わせ | 読み取りパフォーマンスを確保するためにRead Modelを別途構築するのが実務の基本形 |
| コスト管理 | スナップショットで読み取りコストを削減、古いイベントはAmazon S3 Glacierにアーカイブ |
| 注意点 | イベントスキーマの後方互換性・べき等性・配信順序の設計を最初から考慮する |
イベントソーシングは「複雑すぎる」と敬遠されがちですが、マイクロサービスの障害対策と監査要件を同時に解決できる強力なパターンです。まず小さなサービス(注文管理など単一ドメイン)で試験導入し、スナップショット戦略とCQRSの組み合わせに慣れてから段階的に拡大するのが現場での正解です。
Linuxサーバーの基礎については、姉妹サイトLinuxMaster.JPで詳しく解説しています。
マイクロサービスの設計パターンをもっと体系的に学びたいですか?
クラウド実務に役立つ「Cloud Architecture」カテゴリの記事を他にもまとめています。あわせて読みたい関連記事はこちらからどうぞ。
