

翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。

# AWS Step Functions を使用して、サーバーレス Saga パターンを実装する
<a name="implement-the-serverless-saga-pattern-by-using-aws-step-functions"></a>

*Amazon Web Services、Tabby Ward、Joe Kern、および Rohan Mehta*

## 概要
<a name="implement-the-serverless-saga-pattern-by-using-aws-step-functions-summary"></a>

マイクロサービスアーキテクチャの主な目標は、アプリケーションの俊敏性、柔軟性、および市場投入までの時間を短縮するために、分離された独立したコンポーネントを構築することです。デカップリングにより、各マイクロサービスコンポーネントには独自のデータ永続化レイヤーが設けられます。分散型アーキテクチャでは、ビジネストランザクションが複数のマイクロサービスにまたがる可能性があります。これらのマイクロサービスは単一のアトミック性、一貫性、分離、耐久性 (ACID) トランザクションを使用できないため、部分的なトランザクションになってしまう可能性があります。この場合、すでに処理されたトランザクションを元に戻すには、何らかの制御ロジックが必要です。ディストリビュートサガパターンは、通常、この目的のために使用されます。 

サガパターンは、分散アプリケーションの一貫性を確立し、複数のマイクロサービス間のトランザクションを調整してデータ整合性を維持するのに役立つ障害管理パターンです。サガパターンを使用すると、トランザクションを実行するすべてのサービスがイベントをパブリッシュし、後続のサービスがチェーン内の次のトランザクションを実行するようトリガーします。これはチェーン内の最後のトランザクションが完了するまで続きます。ビジネストランザクションが失敗した場合、sagaは一連の補償トランザクションを組織し、それまでのトランザクションによって行われた変更を取り消します。

このパターンは、AWS Step Functions、AWS Lambda、Amazon DynamoDB などのサーバーレステクノロジーを使用して、サンプルアプリケーション (旅行の予約を処理する) の設定とデプロイを自動化する方法を示しています。サンプルアプリケーションでは、Saga 実行コーディネーターを実装するために Amazon API Gateway と Amazon Simple Notiﬁcation Service (Amazon SNS) も使用します。パターンは、AWS Cloud Development Kit (AWS CDK)、AWS サーバーレスアプリケーションモデル (AWS SAM)、Terraform などのinfrastructure as code (IaC フレームワークを使用してデプロイできます。

## 前提条件と制限事項
<a name="implement-the-serverless-saga-pattern-by-using-aws-step-functions-prereqs"></a>

**前提条件**
+ アクティブな AWS アカウント。
+ AWS CloudFormation スタックを作成するための権限。詳細については、「ACK ドキュメント」の「[Install an ACK Controller](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-iam-template.html)」を参照してください。
+ 任意の IaC フレームワーク (AWS CDK、AWS SAM、または Terraform) を AWS アカウントで設定することで、フレームワーク CLI を使用してアプリケーションをデプロイできます。
+ NodeJSは、アプリケーションの構築とローカルでの実行に使用されます。
+ 任意のコードエディター (Visual Studio Code、Sublime、Atom など)。

**製品バージョン**
+ [NodeJS バージョン 14](https://nodejs.org/en/download/)
+ [AWS CDK バージョン 2.37.1](https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html#getting_started_install)
+ [AWS SAM バージョン 1.71.0](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/install-sam-cli.html)
+ [Terraform バージョン 1.3.7](https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli)

**制限事項**

イベントソーシングは、すべてのコンポーネントがゆるく結合されていて互いに直接認識できないマイクロサービスアーキテクチャに saga オーケストレーションパターンを実装する自然な方法です。トランザクションのステップ数が少ない (3 ～ 5) 場合は、サガパターンが最適かもしれません。ただし、マイクロサービスの数とステップの数が増えるにつれて、複雑さも増します。 

この設計を使用すると、トランザクションパターンをシミュレートするためにすべてのサービスを実行する必要があるため、テストとデバッグが難しくなる可能性があります。

## アーキテクチャ
<a name="implement-the-serverless-saga-pattern-by-using-aws-step-functions-architecture"></a>

**ターゲットアーキテクチャ**

提案されたアーキテクチャでは、AWS Step Functions を使用して、フライトの予約、レンタカーの予約、休暇の支払い処理といったサガパターンを構築しています。

以下のワークフロー図は、旅行予約システムの一般的なフローを示しています。ワークフローは、航空旅行の予約 (「ReserveFlight」)、車の予約 (「ReserveCarRental」)、支払いの処理 (「ProcessPayment」)、フライトの予約の確認 (「ConfirmFlight」)、レンタカーの確認 (「ConfirmCarRental」) で構成され、これらのステップが完了すると成功通知が送られます。ただし、これらのトランザクションのいずれかを実行中にエラーが発生すると、システムは逆方向にフェイルオーバーし始めます。例えば、支払い処理 (「ProcessPayment」) でエラーが発生すると返金 (「RefundPayment」) がトリガーされ、レンタカーとフライトのキャンセル (「CancelRentalReservation」と「CancelFlightReservation」) がトリガーされ、トランザクション全体が失敗メッセージで終了します。

このパターンでは、図で強調表示されているタスクごとに個別の Lambda 関数がデプロイされるほか、フライト、レンタカー、支払い用の 3 つの DynamoDB テーブルもデプロイされます。各 Lambda 関数は、トランザクションが確認されたかロールバックされたかに応じて、それぞれの DynamoDB テーブルの行を作成、更新、または削除します。このパターンでは、Amazon SNS を使用してサブスクライバーにテキスト (SMS) メッセージを送信し、トランザクションが失敗または成功したことを通知します。 

![saga パターンに基づく旅行予約システムのワークフロー。](http://docs.aws.amazon.com/ja_jp/prescriptive-guidance/latest/patterns/images/pattern-img/fec0789c-d9b1-4d80-b179-dd9a7ecbec07/images/daad3e8e-6e6b-41c2-95c1-ca79d53ead64.png)


 

**自動化とスケール**

IaC フレームワークのいずれかを使用して、このアーキテクチャの構成を作成できます。お好みの Iac について、以下のリンクの 1 つを使用します。
+ [AWS CDK によるデプロイ](https://serverlessland.com/workflows/saga-pattern-cdk)
+ [AWS SAM によるデプロイ](https://serverlessland.com/workflows/saga-pattern-sam)
+ [テラフォームによるデプロイ](https://serverlessland.com/workflows/saga-pattern-tf)

## ツール
<a name="implement-the-serverless-saga-pattern-by-using-aws-step-functions-tools"></a>

**AWS サービス**
+ [AWS Step Functions](https://aws.amazon.com/step-functions/)は、AWS Lambda 関数と他のサービスを組み合わせてビジネスクリティカルなアプリケーションを構築できるサーバーレスオーケストレーションサービスです。Step Functions のグラフィカルコンソールでは、アプリケーションのワークフローを一連のイベント駆動型ステップとして確認できます。
+ Amazon DynamoDB は、フルマネージド NoSQL データベースサービスであり、シームレスなスケーラビリティを備えた高速で予測可能なパフォーマンスを提供します。DynamoDB を使用して、任意の量のデータを保存および取得できるデータベーステーブルを作成し、任意のレベルのリクエストトラフィックを処理できます。
+ AWS Lambda はサーバーをプロビジョニングしたり管理しなくてもコードを実行できるコンピューティングサービスです。Lambda は必要に応じてコードを実行し、1 日あたり数個のリクエストから 1 秒あたり数千のリクエストまで自動的にスケールします。
+ Amazon API Gateway は、あらゆる規模の REST、HTTP、WebSocket API を作成、公開、管理、モニタリング、保護するための AWS のサービスです。
+ [Amazon Simple Notiﬁcation Service (Amazon SNS)](https://aws.amazon.com/sns/) は、パブリッシャーからサブスクライバーへのメッセージ配信を提供するマネージドサービスです。
+ 「[AWS Cloud Development Kit (AWS CDK)](https://aws.amazon.com/cdk/)」は、TypeScript、JavaScript、Python、Java、C\#/.Net などの使い慣れたプログラミング言語を使用してクラウドアプリケーションリソースを定義するためのソフトウェア開発フレームワークです。
+ [AWS サーバーレスアプリケーションモデル (AWS SAM)](https://aws.amazon.com/serverless/sam/) は、サーバーレスアプリケーションを構築するためのオープンソースフレームワークです。関数、API、データベース、イベントソースマッピングを表現するための省略構文を提供します。

**コード**

IaC テンプレート (AWS CDK、AWS SAM、または Terraform)、Lambda 関数、DynamoDB テーブルなど、サガパターンを示すサンプルアプリケーションのコードは、次のリンクにあります。最初のエピックの指示に従って、これらをインストールします。
+ [AWS CDK によるデプロイ](https://serverlessland.com/workflows/saga-pattern-cdk)
+ [AWS SAM によるデプロイ](https://serverlessland.com/workflows/saga-pattern-sam)
+ [テラフォームによるデプロイ](https://serverlessland.com/workflows/saga-pattern-tf)

## エピック
<a name="implement-the-serverless-saga-pattern-by-using-aws-step-functions-epics"></a>

### パッケージのインストール、コンパイル、ビルド
<a name="install-packages-compile-and-build"></a>


| タスク | 説明 | 必要なスキル | 
| --- | --- | --- | 
|  npm パッケージをインストールします。 | 新しいディレクトリを作成し、ターミナルでそのディレクトリに移動し、このパターンの前の「*コード*」セクションから選択したGitHub リポジトリをクローンします。<br />`package.json`ファイルが含まれているルートフォルダで、次のコマンドを実行して、すべての Node Package Manager (NPM) パッケージをダウンロードしてインストールします。<pre>npm install</pre> | 開発者、クラウドアーキテクト | 
| Compile Script | ルートフォルダで、次のコマンドを実行して、必要なすべての JavaScript ファイルを作成するように TypeScript トランスパイラーに指示します。<pre>npm run build</pre> | 開発者、クラウドアーキテクト | 
| 変更を監視して再コンパイルする。 | ルートフォルダで、別のターミナルウィンドウで次のコマンドを実行してコードの変更を監視し、変更が検出されたらコードをコンパイルします。<pre>npm run watch</pre> | 開発者、クラウドアーキテクト | 
| ユニットテストを実行します (AWS CDK のみ)。 | AWS CDK を使用している場合は、ルートフォルダで次のコマンドを実行して Jest ユニットテストを実行します。<pre>npm run test</pre> | 開発者、クラウドアーキテクト | 

### ターゲット AWS アカウントにリソースをデプロイ
<a name="deploy-resources-to-the-target-aws-account"></a>


| タスク | 説明 | 必要なスキル | 
| --- | --- | --- | 
| デモスタックを AWS にデプロイします。 | アプリケーションは AWS リージョンに依存しません。プロファイルを使用する場合は、「[AWS コマンドラインインターフェイス (AWS CLI) プロファイル](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html)」または 「[AWS CLI 環境変数](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html)」を使用してリージョンを明示的に宣言する必要があります。ルートフォルダで、次のコマンドを実行してデプロイアセンブリを作成し、デフォルトの AWS アカウントとリージョンにデプロイします。<br />AWS CDK<pre>cdk bootstrap<br />cdk deploy</pre><br />AWS SAM<pre>sam build<br />sam deploy --guided</pre><br />Terraform<pre>terraform init<br />terraform apply</pre><br />この処理は、完了まで数分、またはそれ以上かかる場合があります。このコマンドは、AWS CLI に設定されたデフォルトの認証情報を使用します。<br />デプロイの完了後にコンソールに表示される API ゲートウェイ URL をメモしておきます。この情報は Saga 実行フローをテストする際に必要になります。 | 開発者、クラウドアーキテクト | 
| デプロイされたスタックを現在の状態と比較します。 | ルートフォルダで以下のコマンドを実行して、ソースコードに変更を加えた後、デプロイされたスタックを現在の状態と比較します。<br />AWS CDK<pre>cdk diff</pre><br />AWS SAM<pre>sam deploy</pre><br />Terraform<pre>terraform plan</pre> | 開発者、クラウドアーキテクト | 

### 実行フローのテスト
<a name="test-the-execution-flow"></a>


| タスク | 説明 | 必要なスキル | 
| --- | --- | --- | 
| Saga 実行フローをテストします。 | スタックをデプロイしたときに前のステップで書き留めた API ゲートウェイ URL に移動します。この URL により、ステートマシンが起動します。さまざまな URL パラメータを渡してステートマシンのフローを操作する方法の詳細については、「[追加情報](#implement-the-serverless-saga-pattern-by-using-aws-step-functions-additional)」セクションを参照してください。<br />結果を表示するには、AWS マネジメントコンソールにサインインし、Step Functions コンソールに移動します。ここでは、Saga ステートマシンのすべてのステップを確認できます。DynamoDB テーブルを表示して、挿入、更新、削除されたレコードを確認することもできます。画面を頻繁に更新すると、トランザクションのステータスが`pending`から`confirmed`に変わるのを確認できます。 <br />予約が成功または失敗したときに SMS メッセージを受信するように、`stateMachine.ts`ファイル内のコードを携帯電話番号で更新することで SNS トピックを購読できます。詳細については、「[追加情報](#implement-the-serverless-saga-pattern-by-using-aws-step-functions-additional)」セクションの「*Amazon SNS*」を参照してください。 | 開発者、クラウドアーキテクト | 

### クリーンアップ
<a name="clean-up"></a>


| タスク | 説明 | 必要なスキル | 
| --- | --- | --- | 
| リソースをクリーンアップします。 | このアプリケーションにデプロイされたリソースをクリーンアップするには、次のコマンドの 1 つを使用します。<br />AWS CDK<pre>cdk destroy</pre><br />AWS SAM<pre>sam delete</pre><br />Terraform<pre>terraform destroy</pre> | アプリ開発者、クラウドアーキテクト | 

## 関連リソース
<a name="implement-the-serverless-saga-pattern-by-using-aws-step-functions-resources"></a>

**テクニカルペーパー**
+ [AWS でのマイクロサービスの実装](https://docs.aws.amazon.com/pdfs/whitepapers/latest/microservices-on-aws/microservices-on-aws.pdf)
+ [サーバーレスアプリケーションレンズ](https://docs.aws.amazon.com/wellarchitected/latest/serverless-applications-lens/welcome.html)

**AWS サービスのドキュメント**
+ [AWS CDK の使用開始](https://docs.aws.amazon.com/cdk/latest/guide/getting_started.html)」
+ [AWS SAM の使用開始](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-getting-started.html)
+ [AWS Step Functions](https://docs.aws.amazon.com/step-functions/)
+ [Amazon DynamoDB](https://docs.aws.amazon.com/dynamodb/)
+ [ Lambda](https://docs.aws.amazon.com/lambda/)
+ [Amazon API Gateway](https://docs.aws.amazon.com/apigateway/)
+ [Amazon SNS](https://docs.aws.amazon.com/sns/)

**チュートリアル**
+ [サーバーレスコンピューティングのハンズオンワークショップ](https://aws.amazon.com/serverless-workshops/)

## 追加情報
<a name="implement-the-serverless-saga-pattern-by-using-aws-step-functions-additional"></a>

**コード**

テスト目的で、このパターンは API ゲートウェイ と、Step Functions ステートマシンをトリガーするテスト Lambda 関数をデプロイします。Step Functions を使用すると、「ReserveFlight」、「ReserveCarRental」、「ProcessPayment」、「ConfirmFlight」、「ConfirmCarRental」の障害を模倣する `run_type` パラメータを渡すことで、旅行予約システムの機能を制御できます。

`saga`Lambda 関数 (`sagaLambda.ts`) は API ゲートウェイ URL のクエリパラメータから入力を受け取り、次の JSON オブジェクトを作成し、それをStep Functions に渡して実行させます。

```
let input = {
"trip_id": tripID, //  value taken from query parameter, default is AWS request ID
"depart_city": "Detroit",
"depart_time": "2021-07-07T06:00:00.000Z",
"arrive_city": "Frankfurt",
"arrive_time": "2021-07-09T08:00:00.000Z",
"rental": "BMW",
"rental_from": "2021-07-09T00:00:00.000Z",
"rental_to": "2021-07-17T00:00:00.000Z",
"run_type": runType // value taken from query parameter, default is "success"
};
```

次の URL パラメータを渡すことで、Step Functions ステートマシンのさまざまなフローを試すことができます。
+ **実行成功** ─ https://{api gateway url}
+ **リザーブフライトの失敗** ─ https://{api gateway url}?**runType=failFlightsReservation**
+ **フライト確認の失敗** ─ https://{api gateway url}?**runType=failFlightsConfirmation**
+ **レンタカー予約の失敗** ─ https://{api gateway url}?**runType=failCarRentalReservation**
+ **レンタカー確認の失敗** ─ https：//{API ゲートウェイ url}? **runType=レンタカー確認失敗**
+ **支払い処理失敗** ─ https://{api gateway url}?**runType=failPayment**
+ **トリップ ID を渡す** ─ https://{api gateway url}?**tripID=**{by default, trip ID will be the AWS request ID}

**IaC テンプレート**

リンクされたリポジトリには、サンプル旅行予約アプリケーション全体を作成するのに使用できる IaC テンプレートが含まれています。
+ [AWS CDK によるデプロイ](https://serverlessland.com/workflows/saga-pattern-cdk)
+ [AWS SAM によるデプロイ](https://serverlessland.com/workflows/saga-pattern-sam)
+ [テラフォームによるデプロイ](https://serverlessland.com/workflows/saga-pattern-tf)

**DynamoDB テーブル**

フライト、レンタカー、支払いテーブルのデータモデルは次のとおりです。

```
Flight Data Model:
 var params = {
      TableName: process.env.TABLE_NAME,
      Item: {
        'pk' : {S: event.trip_id},
        'sk' : {S: flightReservationID},
        'trip_id' : {S: event.trip_id},
        'id': {S: flightReservationID},
        'depart_city' : {S: event.depart_city},
        'depart_time': {S: event.depart_time},
        'arrive_city': {S: event.arrive_city},
        'arrive_time': {S: event.arrive_time},
        'transaction_status': {S: 'pending'}
      }
    };

Car Rental Data Model:
var params = {
      TableName: process.env.TABLE_NAME,
      Item: {
        'pk' : {S: event.trip_id},
        'sk' : {S: carRentalReservationID},
        'trip_id' : {S: event.trip_id},
        'id': {S: carRentalReservationID},
        'rental': {S: event.rental},
        'rental_from': {S: event.rental_from},
        'rental_to': {S: event.rental_to},
        'transaction_status': {S: 'pending'}
      }
    };

Payment Data Model:
var params = {
      TableName: process.env.TABLE_NAME,
      Item: {
        'pk' : {S: event.trip_id},
        'sk' : {S: paymentID},
        'trip_id' : {S: event.trip_id},
        'id': {S: paymentID},
        'amount': {S: "750.00"}, // hard coded for simplicity as implementing any monetary transaction functionality is beyond the scope of this pattern
        'currency': {S: "USD"},
        'transaction_status': {S: "confirmed"}
      }
    };
```

**Lambda 関数**

Step Functions でのステートマシンフローと実行をサポートするために、以下の関数が作成されます。
+ **フライトを予約**：フライトを予約するために、`transaction_status`が`pending`であるレコードをDynamoDB Flightsテーブルに挿入します。
+ **フライトを確認**：DynamoDB フライトテーブルのレコードを更新して`transaction_status`を`confirmed`に設定し、フライトを確認します。
+ **フライト予約をキャンセル**：DynamoDB フライトテーブルからレコードを削除して、保留中のフライトをキャンセルします。
+ **レンタカーを予約する**：レンタカーを予約するために、`transaction_status`が`pending`であるレコードをDynamoDB CarRentalsテーブルに挿入します。
+ **レンタカーの確認**：DynamoDB CarRentals テーブルのレコードを更新して`transaction_status`を`confirmed`に設定し、レンタカーを確認します。
+ **レンタカーの予約をキャンセル：**DynamoDB CarRentals テーブルからレコードを削除して、保留中のレンタカーをキャンセルします。
+ **支払い処理**：支払いのレコードを DynamoDB 支払いテーブルに挿入します。
+ **支払いをキャンセル**：DynamoDB ペイメントテーブルから支払いのレコードを削除します。

**Amazon SNS**

サンプルアプリケーションでは、SMS メッセージを送信したり、予約の成功または失敗を顧客に通知したりするために、次のトピックとサブスクリプションを作成します。サンプルアプリケーションのテスト中にテキストメッセージを受信したい場合は、ステートマシン定義ファイル内の有効な電話番号で SMS サブスクリプションを更新してください。

AWS CDK スニペット (次のコードの 2 行目に電話番号を追加)：

```
const topic = new  sns.Topic(this, 'Topic');
topic.addSubscription(new subscriptions.SmsSubscription('+11111111111'));
const snsNotificationFailure = new tasks.SnsPublish(this ,'SendingSMSFailure', {
topic:topic,
integrationPattern: sfn.IntegrationPattern.REQUEST_RESPONSE,
message: sfn.TaskInput.fromText('Your Travel Reservation Failed'),
});
 
const snsNotificationSuccess = new tasks.SnsPublish(this ,'SendingSMSSuccess', {
topic:topic,
integrationPattern: sfn.IntegrationPattern.REQUEST_RESPONSE,
message: sfn.TaskInput.fromText('Your Travel Reservation is Successful'),
});
```

AWS SAM スニペット (`+1111111111`文字列を有効な電話番号に置き換える)：

```
  StateMachineTopic11111111111:
    Type: 'AWS::SNS::Subscription'
    Properties:
      Protocol: sms
      TopicArn:
        Ref: StateMachineTopic
      Endpoint: '+11111111111'
    Metadata:
      'aws:sam:path': SamServerlessSagaStack/StateMachine/Topic/+11111111111/Resource
```

Terraform スニペット (`+111111111`文字列を有効な電話番号に置き換える)：

```
resource "aws_sns_topic_subscription" "sms-target" {
  topic_arn = aws_sns_topic.topic.arn
  protocol  = "sms"
  endpoint  = "+11111111111"
}
```

**予約成功**

以下のフローは、「ReserveFlight」、「ReserveCarRental」、「ProcessPayment」の後に「ConfirmFlight」と「ConfirmCarRental」の順で予約が成功するフローです。予約が成功したことは、SNS トピックの購読者に送信される SMS メッセージを通じてお客様に通知されます。

![saga パターンを使用して Step Functions によって実装された予約の成功例。](http://docs.aws.amazon.com/ja_jp/prescriptive-guidance/latest/patterns/images/pattern-img/fec0789c-d9b1-4d80-b179-dd9a7ecbec07/images/f58c894e-7721-4bc7-8f7d-29f23faa5dc1.png)


**予約失敗**

このフローは、サガ・パターンの失敗の一例です。フライトやレンタカーの予約後に「ProcessPayment」が失敗すると、手順は逆の順序でキャンセルされます。 予約が解除され、SNS トピックのサブスクライバーに送信される SMS メッセージを通じてお客様に障害が通知されます。

![saga パターンを使用して Step Functions によって実装された予約の失敗例。](http://docs.aws.amazon.com/ja_jp/prescriptive-guidance/latest/patterns/images/pattern-img/fec0789c-d9b1-4d80-b179-dd9a7ecbec07/images/7c64d326-be27-42c3-b03f-d677efedb9a7.png)
