Amazon DynamoDB でネストされたトランザクションを実現する C# フレームワーク入門

益子 竜与志
益子 竜与志
XThreads
最終更新日:2026年04月13日公開日:2026年04月13日

Amazon DynamoDB は高速・スケーラブルな NoSQL データベースとして広く採用されていますが、標準 API ではネストされたトランザクションをサポートしていません。本記事では、AWS が公開したオープンソースの C# フレームワーク「amazon-dynamodb-transaction-framework」を活用して、複数レイヤーにまたがる複雑なトランザクションをシンプルかつ安全に実装する方法を解説します。

Amazon DynamoDB は、ミリ秒単位のレスポンスタイムと自動スケーリングを提供する AWS のフルマネージド NoSQL データベースです。マイクロサービスや高トラフィックなウェブアプリケーションのバックエンドとして広く採用されています。一方で、複雑なビジネスロジックを実装する際に「ネストされたトランザクション」が使いたいというニーズが開発現場から多く聞かれます。しかし、DynamoDB の標準 API はネストされたトランザクションを直接サポートしていません。本記事では、AWS が公開した C# 向けのオープンソースフレームワーク「amazon-dynamodb-transaction-framework」を使って、この課題をどのように解決できるかを解説します。

Amazon DynamoDB のトランザクション機能とその制約

DynamoDB のトランザクション機能は、複数のアイテムに対する「すべて成功するか、すべて失敗するか」のアトミックな操作を実現します。これにより、ACID(原子性・一貫性・分離性・永続性)の特性を持つ操作が可能になります。

主要な API は以下の 2 つです。

  • TransactWriteItems: Put / Update / Delete / ConditionCheck を最大 100 件グループ化して一括実行します。合計サイズの上限は 4 MB です。
  • TransactGetItems: Get 操作を最大 100 件グループ化して読み取りトランザクションとして実行します。こちらも最大 4 MB です。

トランザクション利用時のコスト面では注意が必要です。通常の書き込み・読み込みと比較して、WCU(書き込みキャパシティユニット)と RCU(読み込みキャパシティユニット)がそれぞれ 2 倍消費されます。これは DynamoDB が各アイテムに対して「準備」と「コミット」の 2 段階の内部処理を行うためです。

しかし最大の制約は、同一アイテムへの複数操作が禁止されていること、そしてネストされたトランザクションが標準ではサポートされていない点です。複数のビジネスロジックレイヤーでトランザクションを扱う場合、この制限が実装の妨げになることがあります。

ネストされたトランザクションが必要になる理由

シンプルな CRUD 操作では標準の DynamoDB トランザクションで十分ですが、エンタープライズアプリケーションでは状況が変わります。たとえば、以下のようなシナリオを考えてみましょう。

  • 注文処理サービスが在庫サービスと決済サービスをオーケストレーションする場合
  • 各サービスが独自のトランザクション境界を持ちながら、上位の親トランザクションで全体の整合性を保証したい場合
  • 一部の子処理が失敗したときのみ部分ロールバックを実行したい場合

このようなシナリオでは、単一のフラットなトランザクションではなく、「階層的なトランザクション管理」が求められます。また、各サービスレイヤーがトランザクションの詳細を知らずに済むよう、コードの再利用性とモジュール性を高めることも重要です。

従来は、こうした要件を満たすためにアプリケーション側で複雑な状態管理ロジックを実装する必要がありました。しかし、AWS が提供するフレームワークを活用することで、このような複雑さを抽象化できます。

フレームワークのアーキテクチャと主要コンポーネント

ネストされたトランザクションアーキテクチャ図

「amazon-dynamodb-transaction-framework」は、DynamoDB の標準トランザクション API の上に抽象レイヤーを提供する C# ライブラリです。GitHub の aws-samples 組織から MIT-0 ライセンスで公開されており、商用利用も含めて自由に使用できます。

フレームワークの主要コンポーネントは以下の 3 つです。

TransactScope

トランザクションのライフサイクル全体(開始・書き込みアイテム追加・コミット・ロールバック)を管理する中心的なクラスです。内部ではスタック構造でネストを管理しており、次のフィールドを保持しています。

  • _client: DynamoDB クライアント
  • _transactRequest: トランザクション要求オブジェクト
  • _subTransactScope: ネストされた子スコープへの参照
  • _parentTransactScope: 親スコープへの参照

TransactExtensions

DynamoDB のアイテム操作をトランザクションアイテムに変換するユーティリティクラスです。非トランザクション操作とトランザクション操作の間でコードを重複して書く必要がなくなります。単一責任の原則に従い、変換ロジックをこのクラスに集約することで、各ビジネスロジックのコードがすっきりします。

AttributeExtensions

エンティティオブジェクトを DynamoDB の AttributeValue 形式に変換するユーティリティです。C# のオブジェクトから DynamoDB が要求する低レベル表現への変換処理をカプセル化します。

プロジェクト構成は、データアクセス層(BootCamp.DataAccess)、ビジネスロジック層(BootCamp.Service)、メインアプリケーション(BootCampDynamoDB)の 3 層に分かれており、各層が独立してトランザクションを扱える設計になっています。

TransactScope を使ったネストされたトランザクションの実装

C# コードによる TransactScope 実装例

TransactScope の主要なメソッドと動作を見ていきましょう。

Begin() メソッド

Begin() を初めて呼び出すと、新しい TransactWriteItemsRequest が初期化されます。同じスコープで再度呼び出した場合は、子スコープが作成され、その子スコープが親スコープへの参照を保持します。これによりスタック構造が形成され、ネストの深さに応じた階層管理が実現されます。

// 外側(親)トランザクションの開始
var outerScope = new TransactScope(client);
await outerScope.Begin();

// 内側(子)トランザクションの開始
await outerScope.Begin(); // 内部で _subTransactScope が作成される

AddTransactWriteItem() メソッド

トランザクションに書き込みアイテムを追加します。null チェックが実装されており、不正なアイテムが混入するのを防ぎます。現在アクティブなスコープ(最も深い子スコープ)に対してアイテムが追加されます。

Commit() メソッド(LIFO 順)

コミット操作は Last-In-First-Out(後入れ先出し)の順序で実行されます。つまり、最も深い子スコープが最初にコミットされ、その後に親スコープがコミットされます。すべての処理は非同期で行われ、ConfigureAwait(false) を使用してデッドロックを防いでいます。

// コミットは子から親の順(LIFO)
await outerScope.Commit();
// → 内部で子スコープを先にコミット → 親をコミット

Rollback() メソッド(再帰的)

ロールバックも再帰的に実行されます。子スコープがロールバックされた後、親スコープのトランザクション要求が null で初期化されて状態がクリアされます。これにより、失敗した場合でも完全なクリーンアップが保証されます。

try
{
    await outerScope.Begin();
    // ... 処理 ...
    await outerScope.Commit();
}
catch (TransactionCanceledException ex)
{
    await outerScope.Rollback(); // 子から再帰的にロールバック
    throw;
}

エラー処理とベストプラクティス

DynamoDB トランザクションを本番環境で使用する際は、適切なエラー処理が不可欠です。主要な例外クラスとその対処法を以下に示します。

  • TransactionCanceledException: トランザクションがキャンセルされた場合に発生します。条件式の不成立、同一アイテムへの重複操作、キャパシティ不足などが原因として挙げられます。AWS SDK は自動再試行しないため、アプリケーション側での再試行ロジックが必要です。
  • TransactionConflictException: 同一アイテムへの同時アクセスが発生した場合に発生します。指数バックオフを使った再試行が有効です。
  • IdempotentParameterMismatchException: べき等性確保のためのクライアントトークンを使い回した場合に発生します。トークンの有効期限は 10 分です。

運用上のベストプラクティスとして、以下の点を押さえておきましょう。

  • 自動スケーリングを有効化する: トランザクションは通常の 2 倍の WCU/RCU を消費するため、キャパシティ不足に陥りやすくなります。オンデマンドモードまたは自動スケーリングの活用が推奨されます。
  • 不要なトランザクションを避ける: 単一アイテムの更新など、トランザクションが不要なケースでは通常の操作を使用することでコストを抑えられます。
  • 同一アイテムへの同時更新を最小化する: コンフリクトの原因となるため、データモデルを設計する段階でアクセスパターンを考慮することが重要です。
  • グローバルテーブルではリージョン間トランザクション不可: グローバルテーブルを使用している場合、トランザクションは同一リージョン内でのみ有効です。
  • 大量データ取り込みには BatchWriteItem を使用する: トランザクションの制約(100 アイテム・4 MB)の外で大量データを扱う場合は、BatchWriteItem のほうが適しています。

まとめ

本記事では、Amazon DynamoDB でネストされたトランザクションを実現するための C# フレームワーク「amazon-dynamodb-transaction-framework」について解説しました。

DynamoDB の標準 API が提供する TransactWriteItems / TransactGetItems は強力な機能ですが、複数のサービスレイヤーにまたがる複雑なトランザクション管理には限界があります。このフレームワークが提供する TransactScope を活用することで、以下のメリットが得られます。

  • LIFO 順のコミットと再帰的ロールバックによる安全なトランザクション管理
  • 単一責任の原則に従ったモジュール化された実装
  • 各レイヤーが DynamoDB API の詳細を意識せずにトランザクションを扱える抽象化
  • 部分ロールバックを含む柔軟なエラー処理

MIT-0 ライセンスで公開されており、商用利用も含めて自由に導入できます。DynamoDB を活用した C# アプリケーションで複雑なトランザクション要件に直面しているチームは、ぜひ検討してみてください。ソースコードは GitHub の aws-samples/amazon-dynamodb-transaction-framework から参照できます。

IT/DXプロジェクト推進するPMO・コンサル人材を提供しています

AI利活用×高生産性のリソースで、あらゆるIT/DXプロジェクトを一気通貫支援します

詳しく見る →
AI駆動型ITコンサルティング
Careerバナーconsultingバナー