

# DynamoDB をオンラインショップのデータストアとして使用する
<a name="data-modeling-online-shop"></a>

このユースケースでは、オンラインショップ (または e ストア) のデータストアとして DynamoDB を使用する方法について説明しています。

## ユースケース
<a name="data-modeling-schema-online-shop"></a>

オンラインストアでは、ユーザーはさまざまな製品を閲覧し、最終的に購入することができます。生成された請求書に基づいて、お客様はディスカウントコードまたはギフトカードを使用して支払い、残りの金額をクレジットカードで支払うことができます。購入した商品は、複数の倉庫から集荷され、指定された住所に発送されます。オンラインストアの一般的なアクセスパターンは以下のとおりです。
+ 指定された customerId のお客様を取得する
+ 指定された productId の製品を取得する
+ 指定された warehouseId の倉庫を取得する
+ productId で全倉庫の商品在庫を取得する
+ 指定された OrderId の注文を取得する
+ 指定された OrderID のすべての商品を取得する
+ 指定された OrderID の請求書を取得する
+ 指定された OrderID のすべての出荷情報を取得する
+ 指定された日付範囲における指定された productId のすべての注文を取得する
+ 指定された invoiceId の請求書を取得する
+ 指定された InvoiceID のすべての支払いを取得する
+ 指定された shipmentId の出荷詳細を取得する
+ 指定された warehouseId のすべての出荷情報を取得する
+ 指定された warehouseId のすべての商品の在庫を取得する
+ 指定された日付範囲における指定された customerId のすべての請求書を取得する
+ 指定された日付範囲で指定された customerId で注文されたすべての製品を取得する

## エンティティ関係図
<a name="data-modeling-schema-online-shop-erd"></a>

これは、オンラインショップのデータストアとして DynamoDB をモデル化するために使用するエンティティ関係図 (ERD) です。

![製品、注文、支払い、顧客などのエンティティを示す、オンラインストアのデータモデルの ERD。](http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/images/DataModeling/OnlineShop-1-ERD.png)


## アクセスパターン
<a name="data-modeling-schema-online-shop-access-patterns"></a>

以上が、DynamoDB をオンラインショップのデータストアとして利用する場合のアクセスパターンです。

1. `getCustomerByCustomerId`

1. `getProductByProductId`

1. `getWarehouseByWarehouseId`

1. `getProductInventoryByProductId`

1. `getOrderDetailsByOrderId`

1. `getProductByOrderId`

1. `getInvoiceByOrderId`

1. `getShipmentByOrderId`

1. `getOrderByProductIdForDateRange`

1. `getInvoiceByInvoiceId`

1. `getPaymentByInvoiceId`

1. `getShipmentDetailsByShipmentId`

1. `getShipmentByWarehouseId`

1. `getProductInventoryByWarehouseId`

1. `getInvoiceByCustomerIdForDateRange`

1. `getProductsByCustomerIdForDateRange`

## スキーマ設計の進化
<a name="data-modeling-schema-online-shop-design-evolution"></a>

[DynamoDB 用の NoSQL Workbench](workbench.md) を使用して、[AnOnlineShop\_1.json](https://github.com/aws-samples/amazon-dynamodb-design-patterns/blob/master/examples/an-online-shop/json/AnOnlineShop_1.json) をインポートし、`AnOnlineShop` という新しいデータモデルと `OnlineShop` という新しいテーブルを作成します。パーティションキーとソートキーには一般的な名前 `PK` と `SK` を使用することに注意してください。これは、さまざまなタイプのエンティティを同じテーブルに保存するための方法です。

**ステップ 1: アクセスパターン 1 (`getCustomerByCustomerId`) に対処する**

[AnOnlineShop\_2.json](https://github.com/aws-samples/amazon-dynamodb-design-patterns/blob/master/examples/an-online-shop/json/AnOnlineShop_2.json) をインポートして、アクセスパターン 1 (`getCustomerByCustomerId`) を処理します。エンティティの中には他のエンティティとリレーションシップを持たないものもあるため、それらには同じ値の `PK` と `SK` を使用します。データ例では、`customerId` を後で追加される他のエンティティから区別するために、キーに接頭辞 `c#` が使われていることに注意してください。この方法は他のエンティティでも繰り返されます。

このアクセスパターンに対処するために、[https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_GetItem.html](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_GetItem.html) オペレーションを `PK=customerId` と `SK=customerId` で使用できます。

**ステップ 2: アクセスパターン 2 (`getProductByProductId`) に対処する**

[AnOnlineShop\_3.json](https://github.com/aws-samples/amazon-dynamodb-design-patterns/blob/master/examples/an-online-shop/json/AnOnlineShop_3.json) をインポートして、`product` エンティティのアクセスパターン 2 (`getProductByProductId`) に対処します。製品エンティティには `p#` というプレフィックスが付けられ、同じソートキー属性が `customerID` と `productID` の保存にも使われています。一般的な命名規則と[垂直パーティショニング](data-modeling-blocks.md#data-modeling-blocks-vertical-partitioning)によって、このような項目コレクションを作成し、効果的な単一テーブル設計を実現できます。

このアクセスパターンに対処するため、`GetItem` オペレーションは `PK=productId` および `SK=productId` で使用できます。

**ステップ 3: アクセスパターン 3 (`getWarehouseByWarehouseId`) に対処する**

[AnOnlineShop\_4.json](https://github.com/aws-samples/amazon-dynamodb-design-patterns/blob/master/examples/an-online-shop/json/AnOnlineShop_4.json) をインポートして、`warehouse` エンティティのアクセスパターン 3 (`getWarehouseByWarehouseId`) に対処します。現在、`customer`、`product`、および `warehouse` エンティティが同じテーブルに追加されています。これらはプレフィックスと `EntityType` 属性を使用して区別されます。タイプ属性 (またはプレフィックスの命名) は読みやすさを向上させます。さまざまなエンティティの英数字 ID を同じ属性に保存するだけでは、読みやすさに影響が出ます。これらの識別子がないと、あるエンティティを別のエンティティと区別するのが難しくなります。

このアクセスパターンに対処するため、`GetItem` オペレーションは `PK=warehouseId` および `SK=warehouseId` で使用できます。

**ベーステーブル **– 

![ID で倉庫データを取得するためのプレフィックスと EntityType を含む DynamoDB テーブル設計。](http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/images/DataModeling/OnlineShop-2-Step3.png)


**ステップ 4: アクセスパターン 4 (`getProductInventoryByProductId`) に対処する**

[AnOnlineShop\_5.json](https://github.com/aws-samples/amazon-dynamodb-design-patterns/blob/master/examples/an-online-shop/json/AnOnlineShop_5.json) をインポートしてアクセスパターン 4 (`getProductInventoryByProductId`) に対処します。`warehouseItem` エンティティは、各倉庫の商品数を追跡するために使用されます。この項目は通常、倉庫に商品が追加されたり、倉庫から商品が削除されたりすると更新されます。ERD にあるように、`product` と `warehouse` の間には多対多リレーションシップがあります。ここで、`product` から `warehouse` への 1 対多リレーションシップは `warehouseItem` のようにモデル化されます。その後、`warehouse` から `product` への 1 対多リレーションシップもモデル化されます。

アクセスパターン 4 は、`PK=ProductId` および `SK begins_with “w#“` に対するクエリで対処できます。

ソートキーに適用できる `begins_with()` およびその他の式の詳細については、「[キー条件式](Query.KeyConditionExpressions.md)」を参照してください。

**ベーステーブル **– 

![特定の倉庫の製品在庫を追跡するために ProductID と warehouseId をクエリするテーブル設計。](http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/images/DataModeling/OnlineShop-3-Step4.png)


**ステップ 5: アクセスパターン 5 (`getOrderDetailsByOrderId`) と 6 (`getProductByOrderId`) に対処する**

[AnOnlineShop\_6.json](https://github.com/aws-samples/amazon-dynamodb-design-patterns/blob/master/examples/an-online-shop/json/AnOnlineShop_6.json) をインポートして、さらに `customer`、`product`、および `warehouse` の項目をテーブルに追加します。次に、[AnOnlineShop\_7.json](https://github.com/aws-samples/amazon-dynamodb-design-patterns/blob/master/examples/an-online-shop/json/AnOnlineShop_7.json) をインポートして、アクセスパターン 5 (`getOrderDetailsByOrderId`) および 6 (`getProductByOrderId`) に対処できる `order` 用項目コレクションを構築します。orderItem エンティティとしてモデル化された `order` と `product` の間の 1 対多リレーションシップを確認できます。

アクセスパターン 5 (`getOrderDetailsByOrderId`) に対処するには、`PK=orderId` を使用してテーブルをクエリします。これにより、`customerId` および注文された製品を含む注文に関するすべての情報が提供されます。

**ベーステーブル **– 

![orderId を使用してクエリを実行し、注文したすべての製品に関する情報を取得するテーブル設計。](http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/images/DataModeling/OnlineShop-4-Step5.png)


アクセスパターン 6 (`getProductByOrderId`) に対処するには、`order` 内の製品のみを読み取る必要があります。これを達成するために、`PK=orderId` および `SK begins_with “p#”` を使用してテーブルをクエリします。

**ベーステーブル **– 

![orderId と productId を使用してクエリし、注文内の製品を取得するテーブル設計。](http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/images/DataModeling/OnlineShop-5-Step5.png)


**ステップ 6: アクセスパターン 7 (`getInvoiceByOrderId`) に対処する**

[AnOnlineShop\_8.json](https://github.com/aws-samples/amazon-dynamodb-design-patterns/blob/master/examples/an-online-shop/json/AnOnlineShop_8.json) をインポートして、アクセスパターン 7 (`getInvoiceByOrderId`) を処理する注文項目コレクションに `invoice` エンティティを追加します。このアクセスパターンに対処するために、`PK=orderId` および `SK begins_with “i#”` でクエリオペレーションを使用できます。

**ベーステーブル **– 

![orderId で請求書を取得するための請求書エンティティを注文項目コレクション内に含むテーブル設計。](http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/images/DataModeling/OnlineShop-6-Step6.png)


**ステップ 7: アクセスパターン 8 (`getShipmentByOrderId`) に対処する**

[AnOnlineShop\_9.json](https://github.com/aws-samples/amazon-dynamodb-design-patterns/blob/master/examples/an-online-shop/json/AnOnlineShop_9.json) をインポートして、アクセスパターン 8 (`getShipmentByOrderId`) を処理する注文項目コレクションに `shipment` エンティティを追加します。単一テーブルの設計にさらに多くの種類のエンティティを追加することで、同じ垂直分割モデルを拡張しています。注文項目コレクションに、`order` エンティティが `shipment`、`orderItem`、および `invoice` エンティティと持つさまざまな関係がどのように含まれているかに注意してください。

`orderId` による出荷を取得するには、`PK=orderId` と `SK begins_with “sh#”` でクエリオペレーションを実行します。

**ベーステーブル **– 

![注文 ID で出荷を取得するための出荷エンティティが注文項目コレクションに追加されているテーブル設計。](http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/images/DataModeling/OnlineShop-7-Step7.png)


**ステップ 8: アクセスパターン 9 (`getOrderByProductIdForDateRange`) に対処する**

前のステップで、注文項目コレクションを作成しました。このアクセスパターンには新しいルックアップディメンション (`ProductID` および `Date`) があり、テーブル全体をスキャンし、関連するレコードをフィルタリングして、ターゲット項目を取得する必要があります。このアクセスパターンに対処するには、[グローバルセカンダリインデックス (GSI)](GSI.md) を作成する必要があります。[AnOnlineShop\_10.json](https://github.com/aws-samples/amazon-dynamodb-design-patterns/blob/master/examples/an-online-shop/json/AnOnlineShop_10.json) をインポートし、複数の注文項目コレクションから `orderItem` データを取得できる GSI を使用して、新しい項目コレクションを作成します。データには `GSI1-PK` と `GSI1-SK` が含まれ、それぞれ `GSI1` のパーティションキーとソートキーになります。

DynamoDB は、GSI のキー属性を含む項目をテーブルから GSI に自動的に入力します。GSI に追加の挿入を手動で行う必要はありません。

アクセスパターン 9 に対処するには、`GSI1-PK=productId` および `GSI1SK between (date1, date2)` を使用して `GSI1` でクエリを実行します。

**ベーステーブル **– 

![複数の注文項目コレクションから注文データを取得するための GSI を含むテーブル設計。](http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/images/DataModeling/OnlineShop-8-Step8-Base.png)


**GSI1:**

![製品 ID と日付で注文を取得するための ProductID と Date をパーティションキーおよびソートキーとして含む GSI 設計。](http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/images/DataModeling/OnlineShop-9-Step8-GSI.png)


**ステップ 9: アクセスパターン 10 (`getInvoiceByInvoiceId`) と 11 (`getPaymentByInvoiceId`) に対処する**

[AnOnlineShop\_11.json](https://github.com/aws-samples/amazon-dynamodb-design-patterns/blob/master/examples/an-online-shop/json/AnOnlineShop_11.json) をインポートして、アクセスパターン 10 (`getInvoiceByInvoiceId`) と 11 (`getPaymentByInvoiceId`) に対処します。どちらも `invoice` に関連しています。これらは 2 つの異なるアクセスパターンですが、同じキー条件を使用して実現されます。`Payments` は、`invoice` エンティティのマップデータタイプを持つ属性として定義されます。

**注記**  
`GSI1-PK` と `GSI1-SK` は、さまざまなエンティティに関する情報を保存するためにオーバーロードされているため、同じ GSI から複数のアクセスパターンに対応できます。GSI オーバーロードの詳細については、「[DynamoDB のグローバルセカンダリインデックスの多重定義](bp-gsi-overloading.md)」を参照してください。

アクセスパターン 10 と 11 に対処するには、`GSI1-PK=invoiceId` および `GSI1-SK=invoiceId` を使用して `GSI1` に対してクエリを実行します。

**GSI1:**

![請求書 ID で請求書と支払いを取得するための invoiceId をパーティションキーおよびソートキーの両方として含む GSI 設計。](http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/images/DataModeling/OnlineShop-10-Step9.png)


**ステップ 10: アクセスパターン 12 (`getShipmentDetailsByShipmentId`) と 13 (`getShipmentByWarehouseId`) に対処する**

[AnOnlineShop\_12.json](https://github.com/aws-samples/amazon-dynamodb-design-patterns/blob/master/examples/an-online-shop/json/AnOnlineShop_12.json) をインポートして、アクセスパターン 12 (`getShipmentDetailsByShipmentId`) と 13 (`getShipmentByWarehouseId`) に対処します。

1 回のクエリオペレーションで注文に関するすべての詳細を取得できるように、`shipmentItem` エンティティがベーステーブルの注文項目コレクションに追加されることに注意してください。

**ベーステーブル **– 

![すべての注文の詳細を取得するための shipmentItem エンティティを注文項目コレクション内に含むテーブル設計。](http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/images/DataModeling/OnlineShop-11-Step10.png)


`GSI1` パーティションとソートキーは、`shipment` と `shipmentItem` の間の 1 対多リレーションシップをモデル化するために既に使用されています。アクセスパターン 12 (`getShipmentDetailsByShipmentId`) に対処するには、`GSI1-PK=shipmentId` および `GSI1-SK=shipmentId` を使用して `GSI1` に対してクエリを実行します。

**GSI1:**

![出荷 ID で出荷の詳細を取得するための shipmentId をパーティションおよびソートキーとして含む GSI1 設計。](http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/images/DataModeling/OnlineShop-12-Step10-GSI.png)


アクセスパターン 13 (`getShipmentByWarehouseId`) の `warehouse` と `shipment` の間の新しい 1 対多リレーションシップをモデル化するには、別の GSI (`GSI2`) を作成する必要があります。アクセスパターンに対処するには、`GSI2-PK=warehouseId` および `GSI2-SK begins_with “sh#”` を使用して `GSI2` に対してクエリを実行します。

**GSI2:**

![倉庫別の出荷を取得するための warehouseId と shipmentId をパーティションキーおよびソートキーとして含む GSI2 設計。](http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/images/DataModeling/OnlineShop-13-Step10-GSI2.png)


**ステップ 11: アクセスパターン 14 (`getProductInventoryByWarehouseId`)、15 (`getInvoiceByCustomerIdForDateRange`)、および 16 (`getProductsByCustomerIdForDateRange`) に対処する**

[AnOnlineShop\_13.json](https://github.com/aws-samples/amazon-dynamodb-design-patterns/blob/master/examples/an-online-shop/json/AnOnlineShop_13.json) をインポートして、次のアクセスパターンセットに関連するデータを追加します。アクセスパターン 14 (`getProductInventoryByWarehouseId`) に対処するには、`GSI2-PK=warehouseId` および `GSI2-SK begins_with “p#”` を使用して `GSI2` に対してクエリを実行します。

**GSI2:**

![アクセスパターン 14 に対処するための warehouseId と productId をパーティションキーおよびソートキーとして含む GSI2 設計。](http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/images/DataModeling/OnlineShop-14-Step11-GSI2.png)


アクセスパターン 15 (`getInvoiceByCustomerIdForDateRange`) に対処するには、`GSI2-PK=customerId` および `GSI2-SK between (i#date1, i#date2)` を使用して `GSI2` に対してクエリを実行します。

**GSI2:**

![アクセスパターン 15 に対処するための customerId と請求書の日付範囲をパーティションキーおよびソートキーとして含む GSI2 設計。](http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/images/DataModeling/OnlineShop-15-Step11-GSI2.png)


アクセスパターン 16 (`getProductsByCustomerIdForDateRange`) に対処するには、`GSI2-PK=customerId` および `GSI2-SK between (p#date1, p#date2)` を使用して `GSI2` に対してクエリを実行します。

**GSI2:**

![アクセスパターン 16 に対処するための customerId と製品の日付範囲をパーティションキーおよびソートキーとして含む GSI2 設計](http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/images/DataModeling/OnlineShop-16-Step11-GSI2.png)


**注記**  
[NoSQL Workbench](workbench.md) では、ファセットは DynamoDB に対するアプリケーションのさまざまなデータアクセスパターンを表します。ファセットを使用すると、ファセットの制約を満たさないレコードを表示することなく、テーブル内のデータのサブセットを表示できます。プロビジョンドキャパシティの引き上げがファセットはビジュアルデータモデリングツールと見なされ、アクセスパターンのモデリングを純粋に補助するものであるため、DynamoDB で使用できるコンストラクトとしては存在しません。  
[AnOnlineShop\_facets.json](https://github.com/aws-samples/amazon-dynamodb-design-patterns/blob/master/examples/an-online-shop/json/AnOnlineShop_facets.json) をインポートして、このユースケースのファセットを確認します。

すべてのアクセスパターンと各アクセスパターンにスキーマ設計で対処する方法を次の表にまとめています。


| アクセスパターン | ベーステーブル/GSI/LSI | Operation | パーティションキー値 | ソートキー値 | 
| --- | --- | --- | --- | --- | 
| getCustomerByCustomerId | ベーステーブル | GetItem |  PK=customerId | SK=customerId | 
| getProductByProductId | ベーステーブル | GetItem |  PK=productId | SK=productId | 
| getWarehouseByWarehouseId | ベーステーブル | GetItem |  PK=warehouseId | SK=warehouseId | 
| getProductInventoryByProductId | ベーステーブル | クエリ |  PK=productId | SK begins\_with "w\#" | 
| getOrderDetailsByOrderId | ベーステーブル | クエリ |  PK=orderId |  | 
| getProductByOrderId | ベーステーブル | クエリ |  PK=orderId | SK begins\_with "p\#" | 
| getInvoiceByOrderId |  ベーステーブル | クエリ |  PK=orderId | SK begins\_with "i\#" | 
| getShipmentByOrderId |  ベーステーブル | クエリ |  PK=orderId | SK begins\_with "sh\#" | 
| getOrderByProductIdForDateRange |  GSI1 | クエリ |  PK=productId | SK between date1 and date2 | 
| getInvoiceByInvoiceId |  GSI1 | クエリ |  PK=invoiceId | SK=invoiceId | 
| getPaymentByInvoiceId |  GSI1 | クエリ |  PK=invoiceId | SK=invoiceId | 
| getShipmentDetailsByShipmentId |  GSI1 | クエリ |  PK=shipmentId | SK=shipmentId | 
| getShipmentByWarehouseId |  GSI2 | クエリ |  PK=warehouseId | SK begins\_with "sh\#" | 
| getProductInventoryByWarehouseId |  GSI2 | クエリ |  PK=warehouseId | SK begins\_with "p\#" | 
| getInvoiceByCustomerIdForDateRange |  GSI2 | クエリ |  PK=customerId | SK between i\#date1 and i\#date2 | 
| getProductsByCustomerIdForDateRange |  GSI2 | クエリ |  PK=customerId | SK between p\#date1 and p\#date2 | 

### オンラインショップの最終スキーマ
<a name="data-modeling-schema-online-store-final-schema"></a>

最終的なスキーマ設計は次のとおりです。このスキーマ設計を JSON ファイルとしてダウンロードするには、GitHub の「[DynamoDB 設計パターン](https://github.com/aws-samples/aws-dynamodb-examples/tree/master/schema_design/SchemaExamples)」を参照してください。

**ベーステーブル** – 

![EntityName や Name などの属性を持つオンラインショップのベーステーブルの最終スキーマ。](http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/images/DataModeling/OnlineShop-17-Final-BaseTable.png)


**GSI1**

![EntityType などの属性を持つオンラインショップのベーステーブルの最終 GSI1 スキーマ。](http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/images/DataModeling/OnlineShop-18-Final-GSI1.png)


**GSI2**

![EntityType などの属性を持つオンラインショップのベーステーブルの最終 GSI2 スキーマ。](http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/images/DataModeling/OnlineShop-19-Final-GSI2.png)


## このスキーマ設計での NoSQL Workbench の使用
<a name="data-modeling-schema-online-shop-nosql"></a>

この最終スキーマを、DynamoDB のデータモデリング、データ視覚化、クエリ開発機能を提供するビジュアルツールである [NoSQL Workbench](workbench.md) にインポートして、新しいプロジェクトを詳しく調べたり編集したりできます。使用を開始するには、次の手順に従います。

1. NoSQL Workbench をダウンロードします。詳細については、「[DynamoDB 用の NoSQL Workbench のダウンロード](workbench.settingup.md)」を参照してください。

1. 上記の JSON スキーマファイルをダウンロードします。このファイルは既に NoSQL Workbench モデル形式になっています。

1. JSON スキーマファイルを NoSQL Workbench にインポートします。詳細については、「[既存のデータモデルのインポート](workbench.Modeler.ImportExisting.md)」を参照してください。

1. NOSQL Workbench にインポートしたら、データモデルを編集できます。詳細については、「[既存のデータモデルの編集](workbench.Modeler.Edit.md)」を参照してください。