

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# 使用 DynamoDB 做為線上商店的資料存放區
<a name="data-modeling-online-shop"></a>

此使用案例說明如何將 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/zh_tw/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\$11.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\$12.json](https://github.com/aws-samples/amazon-dynamodb-design-patterns/blob/master/examples/an-online-shop/json/AnOnlineShop_2.json) 處理存取模式 1 (`getCustomerByCustomerId`)。某些實體與其他實體並無關聯，因此我們將針對這些實體使用相同的 `PK` 和 `SK` 值。在範例資料中，請注意索引鍵字首為 `c#`，以和稍後會從其他實體新增的 `customerId` 進行區別。也請於其他實體重複執行此做法。

若要處理此存取模式，可以利用 `PK=customerId` 和 `SK=customerId` 執行 [https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_GetItem.html](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_GetItem.html) 操作。

**步驟 2：位址存取模式 2 (`getProductByProductId`)**

匯入 [AnOnlineShop\$13.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)建立項目集合，可設計有效的單一資料表。

若要處理此存取模式，可以利用 `PK=productId` 和 `SK=productId` 執行 `GetItem` 操作。

**步驟 3：位址存取模式 3 (`getWarehouseByWarehouseId`)**

匯入 [AnOnlineShop\$14.json](https://github.com/aws-samples/amazon-dynamodb-design-patterns/blob/master/examples/an-online-shop/json/AnOnlineShop_4.json)，針對 `getWarehouseByWarehouseId` 實體處理存取模式 3 (`warehouse`)。目前我們已將 `customer`、`product` 與 `warehouse` 實體加入同一個資料表，且這些實體均使用字首和 `EntityType` 屬性。類型屬性 (或字首命名) 能提升模型的可讀性。如果只單純將不同實體的英數 ID 儲存在同一屬性中，閱讀上會相當困難。若少了識別符，就會難以分辨不同實體。

若要處理此存取模式，可以利用 `PK=warehouseId` 和 `SK=warehouseId` 執行 `GetItem` 操作。

**基礎資料表：**

![\[DynamoDB 資料表設計包含字首和 EntityType，可依其 ID 取得倉儲資料。\]](http://docs.aws.amazon.com/zh_tw/amazondynamodb/latest/developerguide/images/DataModeling/OnlineShop-2-Step3.png)


**步驟 4：位址存取模式 4 (`getProductInventoryByProductId`)**

匯入 [AnOnlineShop\$15.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` 的一對多關係已建立模型 `warehouseItem`。接著，從 `product` 至 `warehouse` 的一對多關係也會進行建模。

可透過查詢 `PK=ProductId` 和 `SK begins_with “w#“` 處理存取模式 4。

關於 `begins_with()` 和其他可套用至排序索引鍵的表達式，詳細資訊請參閱[索引鍵條件表達式](Query.KeyConditionExpressions.md)。

**基礎資料表：**

![\[用於查詢 ProductID 和 warehouseId 的資料表設計，以追蹤特定倉儲中的產品庫存。\]](http://docs.aws.amazon.com/zh_tw/amazondynamodb/latest/developerguide/images/DataModeling/OnlineShop-3-Step4.png)


**步驟 5：位址存取模式 5 (`getOrderDetailsByOrderId`) 和 6 (`getProductByOrderId`)**

透過匯入 [AnOnlineShop\$16.json](https://github.com/aws-samples/amazon-dynamodb-design-patterns/blob/master/examples/an-online-shop/json/AnOnlineShop_6.json)，將更多 `customer`、`product` 和 `warehouse` 項目新增至資料表。然後，匯入 [AnOnlineShop\$17.json](https://github.com/aws-samples/amazon-dynamodb-design-patterns/blob/master/examples/an-online-shop/json/AnOnlineShop_7.json)，針對 `order` 建立項目集合以處理存取模式 5 (`getOrderDetailsByOrderId`) 和 6 (`getProductByOrderId`)。您可以看到 `order` 和 `product` 之間的一對多關係已建模為 orderItem 實體。

若想處理存取模式 5 (`getOrderDetailsByOrderId`)，請透過 `PK=orderId` 查詢資料表，了解與訂單相關的所有資訊，包括 `customerId` 和已訂購產品。

**基礎資料表：**

![\[使用 orderId 查詢的資料表設計，以取得所有已訂購產品的相關資訊。\]](http://docs.aws.amazon.com/zh_tw/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/zh_tw/amazondynamodb/latest/developerguide/images/DataModeling/OnlineShop-5-Step5.png)


**步驟 6：位址存取模式 7 (`getInvoiceByOrderId`)**

匯入 [AnOnlineShop\$18.json](https://github.com/aws-samples/amazon-dynamodb-design-patterns/blob/master/examples/an-online-shop/json/AnOnlineShop_8.json)，新增 `invoice` 實體到*訂單*項目集合，以處理存取模式 7 (`getInvoiceByOrderId`)。若要處理此存取模式，可以利用 `PK=orderId` 和 `SK begins_with “i#”` 執行查詢操作。

**基礎資料表：**

![\[訂單項目集合中包含發票實體的資料表設計，以依據 orderId 取得發票。\]](http://docs.aws.amazon.com/zh_tw/amazondynamodb/latest/developerguide/images/DataModeling/OnlineShop-6-Step6.png)


**步驟 7：位址存取模式 8 (`getShipmentByOrderId`)**

匯入 [AnOnlineShop\$19.json](https://github.com/aws-samples/amazon-dynamodb-design-patterns/blob/master/examples/an-online-shop/json/AnOnlineShop_9.json)，新增 `shipment` 實體到*訂單*項目集合，以處理存取模式 8 (`getShipmentByOrderId`)。我們可透過在單一資料表設計中新增更多實體類型，擴展同一個的垂直分割模型。請注意，*訂單*項目集合中項目間的關係，並不同於 `order`、`shipment`、`orderItem` 及 `invoice` 實體間所擁有的關係。

若想透過 `orderId` 取得運送貨物，您可以利用 `PK=orderId` 和 `SK begins_with “sh#”` 執行查詢操作。

**基礎資料表：**

![\[將包含運送貨物實體的資料表設計新增至訂單項目集合，以依訂單 ID 取得運送貨物。\]](http://docs.aws.amazon.com/zh_tw/amazondynamodb/latest/developerguide/images/DataModeling/OnlineShop-7-Step7.png)


**步驟 8：位址存取模式 9 (`getOrderByProductIdForDateRange`)**

在上個步驟中，我們建立了*訂單*項目集合。此存取模式具有新的查詢維度 (`ProductID` 和 `Date`)，會要求您掃描整個資料表並篩選相關記錄，以擷取目標項目。為處理這類存取模式，必須建立[全域次要索引 (GSI)](GSI.md)。匯入 [AnOnlineShop\$110.json](https://github.com/aws-samples/amazon-dynamodb-design-patterns/blob/master/examples/an-online-shop/json/AnOnlineShop_10.json)，利用 GSI 建立新的項目集合，以從數個*訂單*項目集合擷取 `orderItem` 資料。目前資料內有 `GSI1-PK` 和 `GSI1-SK`，分別為 `GSI1` 的分割區索引鍵和排序索引鍵。

DynamoDB 會自動將資料表中包含 GSI 索引鍵屬性的項目填入 GSI，不須額外手動插入 GSI。

若想處理存取模式 9，請利用 `GSI1-PK=productId` 和 `GSI1SK between (date1, date2)` 查詢 `GSI1`。

**基礎資料表：**

![\[包含 GSI 的資料表設計，可從數個訂單項目集合取得訂單資料。\]](http://docs.aws.amazon.com/zh_tw/amazondynamodb/latest/developerguide/images/DataModeling/OnlineShop-8-Step8-Base.png)


**GSI1：**

![\[使用 ProductID 和 Date 做為分割區和排序索引鍵的 GSI 設計，依產品 ID 和日期取得訂單。\]](http://docs.aws.amazon.com/zh_tw/amazondynamodb/latest/developerguide/images/DataModeling/OnlineShop-9-Step8-GSI.png)


**步驟 9：位址存取模式 10 (`getInvoiceByInvoiceId`) 和 11 (`getPaymentByInvoiceId`)**

匯入 [AnOnlineShop\$111.json](https://github.com/aws-samples/amazon-dynamodb-design-patterns/blob/master/examples/an-online-shop/json/AnOnlineShop_11.json)，處理與 `invoice` 相關的存取模式 10 (`getInvoiceByInvoiceId`) 和 11 (`getPaymentByInvoiceId`)。即使這兩種存取模式並不相同，仍須透過索引鍵條件以實現。`Payments` 的定義為 `invoice` 實體上具地圖資料類型的屬性。

**注意**  
為了儲存不同實體的資訊，`GSI1-PK` 和 `GSI1-SK` 會進行多載，以便從同一個 GSI 取得多個存取模式。如需 GSI 多載的詳細資訊，請參閱 [在 DynamoDB 中多載全域次要索引](bp-gsi-overloading.md)。

若想處理存取模式 10 和 11，請利用 `GSI1-PK=invoiceId` 和 `GSI1-SK=invoiceId` 查詢 `GSI1`。

**GSI1：**

![\[以 invoiceId 做為分割區和排序索引鍵的 GSI 設計，以依發票 ID 取得發票和付款。\]](http://docs.aws.amazon.com/zh_tw/amazondynamodb/latest/developerguide/images/DataModeling/OnlineShop-10-Step9.png)


**步驟 10：位址存取模式 12 (`getShipmentDetailsByShipmentId`) 和 13 (`getShipmentByWarehouseId`)**

匯入 [AnOnlineShop\$112.json](https://github.com/aws-samples/amazon-dynamodb-design-patterns/blob/master/examples/an-online-shop/json/AnOnlineShop_12.json)，處理存取模式 12 (`getShipmentDetailsByShipmentId`) 和 13 (`getShipmentByWarehouseId`)。

請注意，`shipmentItem` 實體已新增至基礎資料表上的*訂單*項目集合，以在單一查詢作業中擷取訂單的所有詳細資訊。

**基礎資料表：**

![\[訂單項目集合中包含 shipmentItem 實體的資料表設計，以取得所有訂單詳細資訊。\]](http://docs.aws.amazon.com/zh_tw/amazondynamodb/latest/developerguide/images/DataModeling/OnlineShop-11-Step10.png)


`GSI1` 分割區和排序索引鍵已用於建立 `shipment` 和 `shipmentItem` 之間的一對多關係模型。若想處理存取模式 12 (`getShipmentDetailsByShipmentId`)，請利用 `GSI1-PK=shipmentId` 和 `GSI1-SK=shipmentId` 查詢 `GSI1`。

**GSI1：**

![\[使用 shipmentId 做為分割區和排序索引鍵的 GSI1 設計，依運送貨物 ID 取得運送貨物詳細資訊。\]](http://docs.aws.amazon.com/zh_tw/amazondynamodb/latest/developerguide/images/DataModeling/OnlineShop-12-Step10-GSI.png)


針對存取模式 13 (`getShipmentByWarehouseId`)，我們必須建立另一個 GSI (`GSI2`)，來為 `warehouse` 和 `shipment` 之間新的一對多關係建立模型。若想處理此存取模式，請利用 `GSI2-PK=warehouseId` 和 `GSI2-SK begins_with “sh#”` 查詢 `GSI2`。

**GSI2：**

![\[使用 warehouseId 和 shipmentId 做為分割區和排序索引鍵的 GSI2 設計，依倉儲取得運送貨物。\]](http://docs.aws.amazon.com/zh_tw/amazondynamodb/latest/developerguide/images/DataModeling/OnlineShop-13-Step10-GSI2.png)


**步驟 11：位址存取模式 14 (`getProductInventoryByWarehouseId`)、15 (`getInvoiceByCustomerIdForDateRange`) 和 16 (`getProductsByCustomerIdForDateRange`)**

匯入 [AnOnlineShop\$113.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：**

![\[使用 warehouseId 和 productId 做為分割區和排序索引鍵的 GSI2 設計，以處理存取模式 14。\]](http://docs.aws.amazon.com/zh_tw/amazondynamodb/latest/developerguide/images/DataModeling/OnlineShop-14-Step11-GSI2.png)


若想處理存取模式 15 (`getInvoiceByCustomerIdForDateRange`)，請利用 `GSI2-PK=customerId` 和 `GSI2-SK between (i#date1, i#date2)` 查詢 `GSI2`。

**GSI2：**

![\[使用 customerId 和發票日期範圍做為分割區和排序索引鍵的 GSI2 設計，以處理存取模式 15。\]](http://docs.aws.amazon.com/zh_tw/amazondynamodb/latest/developerguide/images/DataModeling/OnlineShop-15-Step11-GSI2.png)


若想處理存取模式 16 (`getProductsByCustomerIdForDateRange`)，請利用 `GSI2-PK=customerId` 和 `GSI2-SK between (p#date1, p#date2)` 查詢 `GSI2`。

**GSI2：**

![\[使用 customerId 和產品日期範圍做為分割區和排序索引鍵的 GSI2 設計，以處理存取模式 16\]](http://docs.aws.amazon.com/zh_tw/amazondynamodb/latest/developerguide/images/DataModeling/OnlineShop-16-Step11-GSI2.png)


**注意**  
在 [NoSQL Workbench](workbench.md) 中，*面向*代表適用於 DynamoDB 的應用程式不同資料存取模式。面向為您提供了一種查看資料表中的資料子集的方法，而無需查看不符合面向限制的記錄。面向是一種視覺資料建模工具，在 DynamoDB 中不作為可用的建構存在，因為其純粹用於輔助存取模式建模。  
匯入 [AnOnlineShop\$1facets.json](https://github.com/aws-samples/amazon-dynamodb-design-patterns/blob/master/examples/an-online-shop/json/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\$1with "w\$1" | 
| getOrderDetailsByOrderId | 基礎資料表 | Query |  PK=orderId |  | 
| getProductByOrderId | 基礎資料表 | Query |  PK=orderId | SK begins\$1with "p\$1" | 
| getInvoiceByOrderId |  基礎資料表 | Query |  PK=orderId | SK begins\$1with "i\$1" | 
| getShipmentByOrderId |  基礎資料表 | Query |  PK=orderId | SK begins\$1with "sh\$1" | 
| 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\$1with "sh\$1" | 
| getProductInventoryByWarehouseId |  GSI2 | Query |  PK=warehouseId | SK begins\$1with "p\$1" | 
| getInvoiceByCustomerIdForDateRange |  GSI2 | Query |  PK=customerId | SK between i\$1date1 and i\$1date2 | 
| getProductsByCustomerIdForDateRange |  GSI2 | Query |  PK=customerId | SK between p\$1date1 and p\$1date2 | 

### 線上商店最終結構描述
<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/zh_tw/amazondynamodb/latest/developerguide/images/DataModeling/OnlineShop-17-Final-BaseTable.png)


**GSI1**

![\[具有 EntityType 等屬性的線上商店基礎資料表最終 GSI1 結構描述。\]](http://docs.aws.amazon.com/zh_tw/amazondynamodb/latest/developerguide/images/DataModeling/OnlineShop-18-Final-GSI1.png)


**GSI2**

![\[具有 EntityType 等屬性的線上商店基礎資料表最終 GSI2 結構描述。\]](http://docs.aws.amazon.com/zh_tw/amazondynamodb/latest/developerguide/images/DataModeling/OnlineShop-19-Final-GSI2.png)


## 使用 NoSQL Workbench 與此結構描述設計
<a name="data-modeling-schema-online-shop-nosql"></a>

您可以將此最終結構描述匯入 [NoSQL Workbench](workbench.md)，這是為 DynamoDB 提供資料建模、資料視覺化，和查詢開發功能的視覺化工具，以進一步探索和編輯新專案。請依照下列步驟以開始使用：

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)。