DynamoDB をオンラインショップのデータストアとして使用する
このユースケースでは、オンラインショップ (または e ストア) のデータストアとして DynamoDB を使用する方法について説明しています。
ユースケース
オンラインストアでは、ユーザーはさまざまな製品を閲覧し、最終的に購入することができます。生成された請求書に基づいて、お客様はディスカウントコードまたはギフトカードを使用して支払い、残りの金額をクレジットカードで支払うことができます。購入した商品は、複数の倉庫から集荷され、指定された住所に発送されます。オンラインストアの一般的なアクセスパターンは以下のとおりです。
-
指定された customerId のお客様を取得する
-
指定された productId の製品を取得する
-
指定された warehouseId の倉庫を取得する
-
productId で全倉庫の商品在庫を取得する
-
指定された OrderId の注文を取得する
-
指定された OrderID のすべての商品を取得する
-
指定された OrderID の請求書を取得する
-
指定された OrderID のすべての出荷情報を取得する
-
指定された日付範囲における指定された productId のすべての注文を取得する
-
指定された invoiceId の請求書を取得する
-
指定された InvoiceID のすべての支払いを取得する
-
指定された shipmentId の出荷詳細を取得する
-
指定された warehouseId のすべての出荷情報を取得する
-
指定された warehouseId のすべての商品の在庫を取得する
-
指定された日付範囲における指定された customerId のすべての請求書を取得する
-
指定された日付範囲で指定された customerId で注文されたすべての製品を取得する
エンティティ関係図
これは、オンラインショップのデータストアとして DynamoDB をモデル化するために使用するエンティティ関係図 (ERD) です。
アクセスパターン
以上が、DynamoDB をオンラインショップのデータストアとして利用する場合のアクセスパターンです。
-
getCustomerByCustomerId -
getProductByProductId -
getWarehouseByWarehouseId -
getProductInventoryByProductId -
getOrderDetailsByOrderId -
getProductByOrderId -
getInvoiceByOrderId -
getShipmentByOrderId -
getOrderByProductIdForDateRange -
getInvoiceByInvoiceId -
getPaymentByInvoiceId -
getShipmentDetailsByShipmentId -
getShipmentByWarehouseId -
getProductInventoryByWarehouseId -
getInvoiceByCustomerIdForDateRange -
getProductsByCustomerIdForDateRange
スキーマ設計の進化
DynamoDB 用の NoSQL Workbench を使用して、AnOnlineShop_1.jsonAnOnlineShop という新しいデータモデルと OnlineShop という新しいテーブルを作成します。パーティションキーとソートキーには一般的な名前 PK と SK を使用することに注意してください。これは、さまざまなタイプのエンティティを同じテーブルに保存するための方法です。
ステップ 1: アクセスパターン 1 (getCustomerByCustomerId) に対処する
AnOnlineShop_2.jsongetCustomerByCustomerId) を処理します。エンティティの中には他のエンティティとリレーションシップを持たないものもあるため、それらには同じ値の PK と SK を使用します。データ例では、customerId を後で追加される他のエンティティから区別するために、キーに接頭辞 c# が使われていることに注意してください。この方法は他のエンティティでも繰り返されます。
このアクセスパターンに対処するために、GetItem オペレーションを PK=customerId と SK=customerId で使用できます。
ステップ 2: アクセスパターン 2 (getProductByProductId) に対処する
AnOnlineShop_3.jsonproduct エンティティのアクセスパターン 2 (getProductByProductId) に対処します。製品エンティティには p# というプレフィックスが付けられ、同じソートキー属性が customerID と productID の保存にも使われています。一般的な命名規則と垂直パーティショニングによって、このような項目コレクションを作成し、効果的な単一テーブル設計を実現できます。
このアクセスパターンに対処するため、GetItem オペレーションは PK=productId および SK=productId で使用できます。
ステップ 3: アクセスパターン 3 (getWarehouseByWarehouseId) に対処する
AnOnlineShop_4.jsonwarehouse エンティティのアクセスパターン 3 (getWarehouseByWarehouseId) に対処します。現在、customer、product、および warehouse エンティティが同じテーブルに追加されています。これらはプレフィックスと EntityType 属性を使用して区別されます。タイプ属性 (またはプレフィックスの命名) は読みやすさを向上させます。さまざまなエンティティの英数字 ID を同じ属性に保存するだけでは、読みやすさに影響が出ます。これらの識別子がないと、あるエンティティを別のエンティティと区別するのが難しくなります。
このアクセスパターンに対処するため、GetItem オペレーションは PK=warehouseId および SK=warehouseId で使用できます。
ベーステーブル:
ステップ 4: アクセスパターン 4 (getProductInventoryByProductId) に対処する
AnOnlineShop_5.jsongetProductInventoryByProductId) に対処します。warehouseItem エンティティは、各倉庫の商品数を追跡するために使用されます。この項目は通常、倉庫に商品が追加されたり、倉庫から商品が削除されたりすると更新されます。ERD にあるように、product と warehouse の間には多対多リレーションシップがあります。ここで、product から warehouse への 1 対多リレーションシップは warehouseItem のようにモデル化されます。その後、warehouse から product への 1 対多リレーションシップもモデル化されます。
アクセスパターン 4 は、PK=ProductId および SK begins_with “w#“ に対するクエリで対処できます。
ソートキーに適用できる begins_with() およびその他の式の詳細については、「キー条件式」を参照してください。
ベーステーブル:
ステップ 5: アクセスパターン 5 (getOrderDetailsByOrderId) と 6 (getProductByOrderId) に対処する
AnOnlineShop_6.jsoncustomer、product、および warehouse の項目をテーブルに追加します。次に、AnOnlineShop_7.jsongetOrderDetailsByOrderId) および 6 (getProductByOrderId) に対処できる order 用項目コレクションを構築します。orderItem エンティティとしてモデル化された order と product の間の 1 対多リレーションシップを確認できます。
アクセスパターン 5 (getOrderDetailsByOrderId) に対処するには、PK=orderId を使用してテーブルをクエリします。これにより、customerId および注文された製品を含む注文に関するすべての情報が提供されます。
ベーステーブル:
アクセスパターン 6 (getProductByOrderId) に対処するには、order 内の製品のみを読み取る必要があります。これを達成するために、PK=orderId および SK begins_with “p#” を使用してテーブルをクエリします。
ベーステーブル:
ステップ 6: アクセスパターン 7 (getInvoiceByOrderId) に対処する
AnOnlineShop_8.jsongetInvoiceByOrderId) を処理する注文項目コレクションに invoice エンティティを追加します。このアクセスパターンに対処するために、PK=orderId および SK begins_with
“i#” でクエリオペレーションを使用できます。
ベーステーブル:
ステップ 7: アクセスパターン 8 (getShipmentByOrderId) に対処する
AnOnlineShop_9.jsongetShipmentByOrderId) を処理する注文項目コレクションに shipment エンティティを追加します。単一テーブルの設計にさらに多くの種類のエンティティを追加することで、同じ垂直分割モデルを拡張しています。注文項目コレクションに、order エンティティが shipment、orderItem、および invoice エンティティと持つさまざまな関係がどのように含まれているかに注意してください。
orderId による出荷を取得するには、PK=orderId と SK begins_with “sh#” でクエリオペレーションを実行します。
ベーステーブル:
ステップ 8: アクセスパターン 9 (getOrderByProductIdForDateRange) に対処する
前のステップで、注文項目コレクションを作成しました。このアクセスパターンには新しいルックアップディメンション (ProductID および Date) があり、テーブル全体をスキャンし、関連するレコードをフィルタリングして、ターゲット項目を取得する必要があります。このアクセスパターンに対処するには、グローバルセカンダリインデックス (GSI) を作成する必要があります。AnOnlineShop_10.jsonorderItem データを取得できる GSI を使用して、新しい項目コレクションを作成します。データには GSI1-PK と GSI1-SK が含まれ、それぞれ GSI1 のパーティションキーとソートキーになります。
DynamoDB は、GSI のキー属性を含む項目をテーブルから GSI に自動的に入力します。GSI に追加の挿入を手動で行う必要はありません。
アクセスパターン 9 に対処するには、GSI1-PK=productId および GSI1SK between (date1,
date2) を使用して GSI1 でクエリを実行します。
ベーステーブル:
GSI1:
ステップ 9: アクセスパターン 10 (getInvoiceByInvoiceId) と 11 (getPaymentByInvoiceId) に対処する
AnOnlineShop_11.jsongetInvoiceByInvoiceId) と 11 (getPaymentByInvoiceId) に対処します。どちらも invoice に関連しています。これらは 2 つの異なるアクセスパターンですが、同じキー条件を使用して実現されます。Payments は、invoice エンティティのマップデータタイプを持つ属性として定義されます。
注記
GSI1-PK と GSI1-SK は、さまざまなエンティティに関する情報を保存するためにオーバーロードされているため、同じ GSI から複数のアクセスパターンに対応できます。GSI オーバーロードの詳細については、「DynamoDB のグローバルセカンダリインデックスの多重定義」を参照してください。
アクセスパターン 10 と 11 に対処するには、GSI1-PK=invoiceId および GSI1-SK=invoiceId を使用して GSI1 に対してクエリを実行します。
GSI1:
ステップ 10: アクセスパターン 12 (getShipmentDetailsByShipmentId) と 13 (getShipmentByWarehouseId) に対処する
AnOnlineShop_12.jsongetShipmentDetailsByShipmentId) と 13 (getShipmentByWarehouseId) に対処します。
1 回のクエリオペレーションで注文に関するすべての詳細を取得できるように、shipmentItem エンティティがベーステーブルの注文項目コレクションに追加されることに注意してください。
ベーステーブル:
GSI1 パーティションとソートキーは、shipment と shipmentItem の間の 1 対多リレーションシップをモデル化するために既に使用されています。アクセスパターン 12 (getShipmentDetailsByShipmentId) に対処するには、GSI1-PK=shipmentId および GSI1-SK=shipmentId を使用して GSI1 に対してクエリを実行します。
GSI1:
アクセスパターン 13 (getShipmentByWarehouseId) の warehouse と shipment の間の新しい 1 対多リレーションシップをモデル化するには、別の GSI (GSI2) を作成する必要があります。アクセスパターンに対処するには、GSI2-PK=warehouseId および GSI2-SK
begins_with “sh#” を使用して GSI2 に対してクエリを実行します。
GSI2:
ステップ 11: アクセスパターン 14 (getProductInventoryByWarehouseId)、15 (getInvoiceByCustomerIdForDateRange)、および 16 (getProductsByCustomerIdForDateRange) に対処する
AnOnlineShop_13.jsongetProductInventoryByWarehouseId) に対処するには、GSI2-PK=warehouseId および GSI2-SK
begins_with “p#” を使用して GSI2 に対してクエリを実行します。
GSI2:
アクセスパターン 15 (getInvoiceByCustomerIdForDateRange) に対処するには、GSI2-PK=customerId および GSI2-SK between
(i#date1, i#date2) を使用して GSI2 に対してクエリを実行します。
GSI2:
アクセスパターン 16 (getProductsByCustomerIdForDateRange) に対処するには、GSI2-PK=customerId および GSI2-SK between
(p#date1, p#date2) を使用して GSI2 に対してクエリを実行します。
GSI2:
注記
NoSQL Workbench では、ファセットは DynamoDB に対するアプリケーションのさまざまなデータアクセスパターンを表します。ファセットを使用すると、ファセットの制約を満たさないレコードを表示することなく、テーブル内のデータのサブセットを表示できます。プロビジョンドキャパシティの引き上げがファセットはビジュアルデータモデリングツールと見なされ、アクセスパターンのモデリングを純粋に補助するものであるため、DynamoDB で使用できるコンストラクトとしては存在しません。
AnOnlineShop_facets.json
すべてのアクセスパターンと各アクセスパターンにスキーマ設計で対処する方法を次の表にまとめています。
| アクセスパターン | ベーステーブル/GSI/LSI | 操作 | パーティションキー値 | ソートキー値 |
|---|---|---|---|---|
| getCustomerByCustomerId | ベーステーブル | GetItem | PK=customerId | SK=customerId |
| getProductByProductId | ベーステーブル | GetItem | PK=productId | SK=productId |
| getWarehouseByWarehouseId | ベーステーブル | GetItem | PK=warehouseId | SK=warehouseId |
| getProductInventoryByProductId | ベーステーブル | Query | PK=productId | SK begins_with "w#" |
| getOrderDetailsByOrderId | ベーステーブル | Query | PK=orderId | |
| getProductByOrderId | ベーステーブル | Query | PK=orderId | SK begins_with "p#" |
| getInvoiceByOrderId | ベーステーブル | Query | PK=orderId | SK begins_with "i#" |
| getShipmentByOrderId | ベーステーブル | Query | PK=orderId | SK begins_with "sh#" |
| getOrderByProductIdForDateRange | GSI1 | Query | PK=productId | SK between date1 and date2 |
| getInvoiceByInvoiceId | GSI1 | Query | PK=invoiceId | SK=invoiceId |
| getPaymentByInvoiceId | GSI1 | Query | PK=invoiceId | SK=invoiceId |
| getShipmentDetailsByShipmentId | GSI1 | Query | PK=shipmentId | SK=shipmentId |
| getShipmentByWarehouseId | GSI2 | Query | PK=warehouseId | SK begins_with "sh#" |
| getProductInventoryByWarehouseId | GSI2 | Query | PK=warehouseId | SK begins_with "p#" |
| getInvoiceByCustomerIdForDateRange | GSI2 | Query | PK=customerId | SK between i#date1 and i#date2 |
| getProductsByCustomerIdForDateRange | GSI2 | Query | PK=customerId | SK between p#date1 and p#date2 |
オンラインショップの最終スキーマ
最終的なスキーマ設計は次のとおりです。このスキーマ設計を JSON ファイルとしてダウンロードするには、GitHub の「DynamoDB 設計パターン
ベーステーブル
GSI1
GSI2
このスキーマ設計での NoSQL Workbench の使用
この最終スキーマを、DynamoDB のデータモデリング、データ視覚化、クエリ開発機能を提供するビジュアルツールである NoSQL Workbench にインポートして、新しいプロジェクトを詳しく調べたり編集したりできます。使用を開始するには、次の手順に従います。
-
NoSQL Workbench をダウンロードします。詳細については、「DynamoDB 用の NoSQL Workbench のダウンロード」を参照してください。
-
上記の JSON スキーマファイルをダウンロードします。このファイルは既に NoSQL Workbench モデル形式になっています。
-
JSON スキーマファイルを NoSQL Workbench にインポートします。詳細については、「既存のデータモデルのインポート」を参照してください。
-
NOSQL Workbench にインポートしたら、データモデルを編集できます。詳細については、「既存のデータモデルの編集」を参照してください。
-
データモデルの視覚化、サンプルデータの追加、CSV ファイルからのサンプルデータのインポートを行うには、NoSQL Workbench のデータビジュアライザー機能を使用します。