E-commerce Diagrams
A1 ACCESS PATTERN MATRIX
---
config:
layout: dagre
---
flowchart LR
subgraph SVC["Consumers / Services"]
U1["ECOM-001 UserSvc<br>Get User"]
O2["ECOM-002 OrderSvc<br>List Orders by User"]
O3["ECOM-003 OrderSvc<br>Get Order"]
O4["ECOM-004 OrderSvc<br>List OrderLines"]
B5["ECOM-005 Backoffice<br>Orders by Status/Month"]
P6["ECOM-006 PaymentSvc<br>Get Payments (by Order)"]
P7["ECOM-007 PaymentSvc<br>Find Pending"]
S8["ECOM-008 ShipmentSvc<br>Get Shipments (by Order)"]
I9["ECOM-009 InventorySvc<br>Reserve/Decrement"]
OP10["ECOM-010 Ops<br>Orders Dashboard (status+carrier)"]
AR11["ECOM-011 Archive<br>Export Old Orders"]
end
subgraph PATHS["Physical Access Paths"]
B0["BASE TABLE<br>PK+SK"]
G1["GSI1 OrdersByUser<br>PK: USER#<id><br>SK: <ts>#ORDER#<id>"]
G2["GSI2 OrdersByStatus<br>PK: STATUS#<status>#<yyyy-mm><br>SK: <ts>#<shard>#ORDER#<id>"]
GP["GSI_PAY Pending<br>PK: PAYSTATUS#<status><br>SK: <attempted_at>#ORDER#<id>"]
GS["GSI_SHIP Ops slice<br>PK: SHIP#<status>#<carrier>#<yyyy-mm><br>SK: <updated_at>#ORDER#<id>"]
OFF["Offline / Analytics<br>Export-to-S3 • Athena/Glue"]
end
U1 -- "GetItem<br>PK=USER#id, SK=META" --> B0
O2 -- "Query<br>GSI1PK=USER#id<br>DESC" --> G1
O3 -- "GetItem<br>PK=ORDER#id, SK=META" --> B0
O4 -- "Query<br>PK=ORDER#id + begins_with(SK,'LINE#')<br>ASC" --> B0
B5 -- "Query<br>GSI2PK=STATUS#S#YYYY-MM<br>DESC" --> G2
P6 -- "Query<br>PK=ORDER#id + begins_with(SK,'PAYMENT#')<br>DESC" --> B0
P7 -- Query<br>PAYSTATUS#Pending<br>ASC --> GP
S8 -- "Query<br>PK=ORDER#id + begins_with(SK,'SHIPMENT#')<br>DESC" --> B0
I9 -- "UpdateItem + Condition<br>PK=PRODUCT#id, SK=META" --> B0
OP10 -- Query (ops slice)<br>status+carrier+month --> GS
AR11 -- Periodic export --> OFF
U1:::base
O2:::gsi
O3:::base
O4:::base
B5:::gsi
P6:::base
P7:::gsi
S8:::base
I9:::wr
OP10:::gsi
AR11:::off
B0:::base
G1:::gsi
G2:::gsi
GP:::gsi
GS:::gsi
OFF:::off
classDef base fill:#ecfdf5,stroke:#10b981,color:#064e3b
classDef gsi fill:#fff7ed,stroke:#fb923c,color:#7c2d12
classDef wr fill:#eff6ff,stroke:#60a5fa,color:#0c4a6e
classDef off fill:#f8fafc,stroke:#94a3b8,color:#334155,stroke-dasharray:4 3
A2 KEY DESIGN CHEATSHEET
flowchart TB
%% Styles
classDef base fill:#ecfdf5,stroke:#10b981,rx:6,ry:6,color:#064e3b;
classDef gsi fill:#fff7ed,stroke:#fb923c,rx:6,ry:6,color:#7c2d12;
classDef item fill:#f8fafc,stroke:#94a3b8,rx:6,ry:6,color:#334155;
classDef rule fill:#eff6ff,stroke:#60a5fa,rx:6,ry:6,color:#0c4a6e;
classDef misc fill:#fefce8,stroke:#eab308,rx:6,ry:6,color:#713f12;
subgraph BASE["Base Table Keys"]
BPK["PK formats:
• USER#<user_id>
• ORDER#<order_id>
• PRODUCT#<product_id>"]:::base
BSK["SK formats:
• META
• LINE#<0001..>
• PAYMENT#<iso>
• SHIPMENT#<iso>
• INV#SNAP#<yyyy-mm-dd>"]:::base
end
subgraph GSIs["Global Secondary Indexes"]
G1["GSI1 OrdersByUser
PK: USER#<user_id>
SK: <ts>#ORDER#<order_id> (DESC)"]:::gsi
G2["GSI2 OrdersByStatus
PK: STATUS#<status>#<yyyy-mm>
SK: <ts>#<shard>#ORDER#<order_id> (DESC)"]:::gsi
G3["GSI_PAY PendingPayments (optional)
PK: PAYSTATUS#<status>
SK: <attempted_at>#ORDER#<order_id>"]:::gsi
G4["GSI_SHIP OpsSlice (optional)
PK: SHIP#<status>#<carrier>#<yyyy-mm>
SK: <updated_at>#ORDER#<id>"]:::gsi
end
subgraph ITEMS["Item Types & SK prefixes"]
IU["User
PK=USER#id, SK=META"]:::item
IO["Order (meta)
PK=ORDER#id, SK=META"]:::item
IL["OrderLine
PK=ORDER#id, SK=LINE#<no>"]:::item
IP["Payment
PK=ORDER#id, SK=PAYMENT#<iso>"]:::item
IS["Shipment
PK=ORDER#id, SK=SHIPMENT#<iso>"]:::item
IV["Inventory Snapshot
PK=PRODUCT#id, SK=INV#SNAP#<ymd>"]:::item
end
subgraph RULES["Write Rules / Idempotency / Optimistic Concurrency"]
R1["Create Order:
PutItem IF attribute_not_exists(PK) AND attribute_not_exists(SK)"]:::rule
R2["Add Line:
PutItem IF attribute_not_exists(SK) (idempotent line_no)"]:::rule
R3["Authorize Payment:
PutItem IF attribute_not_exists(SK) (idempotent by timestamp/id)"]:::rule
R4["Inventory Decrement:
Update SET available = available - :q
IF available >= :q"]:::rule
R5["Shipment Update:
Update with version/etag OR condition on current status"]:::rule
end
subgraph MISC["TTL / Streams / Size / Projection"]
T1["TTL (optional):
• old snapshots
• temp reservations (hold TTL)"]:::misc
T2["Streams:
• Project events → outbox/bus
• Build GSI_PAY / metrics
• Backfill sinks"]:::misc
T3["Item size:
• keep < 400KB
• denormalize only hot attrs"]:::misc
T4["Projection:
• GSIs = keys + minimal INCLUDE"]:::misc
end
%% Link the sections
BPK --> BSK
GSIs --> ITEMS
ITEMS --> RULES
RULES --> MISC
A3 ITEM COLLECTIONORDER
flowchart TB
%% A3 Item Collection: PK=ORDER#<id>
classDef head fill:#eef5ff,stroke:#5b8def,stroke-width:1px,rx:6,ry:6;
classDef item fill:#ffffff,stroke:#94a3b8,rx:6,ry:6;
classDef rel fill:#fef9c3,stroke:#f59e0b,rx:6,ry:6;
subgraph PK["PK = ORDER#12345"]
direction TB
META["SK = META \n {order_id,status,placed_at,total_cents,...}"]:::head
L1["SK = LINE#0001 \n {product_id,qty,price_cents}"]:::item
L2["SK = LINE#0002 \n {product_id,qty,price_cents}"]:::item
PAY1["SK = PAYMENT#2025-11-01T10:00Z \n {status,amount_cents,method}"]:::rel
SHIP1["SK = SHIPMENT#2025-11-02T06:00Z \n {carrier,tracking_no,status}"]:::rel
EVT1["SK = EVENT#ORDER_PLACED#2025-11-01T10:00Z"]:::rel
EVT2["SK = EVENT#PAYMENT_AUTH#2025-11-01T10:02Z"]:::rel
end
A4 INDEX OVERLAY
flowchart LR
%% A4 Index Overlay: Base + GSI1 OrdersByUser, GSI2 OrdersByStatus
classDef table fill:#ecfdf5,stroke:#10b981,rx:6,ry:6;
classDef index fill:#f5f3ff,stroke:#8b5cf6,rx:6,ry:6;
classDef proj fill:#ffffff,stroke:#94a3b8,rx:6,ry:6;
subgraph BaseTable["Base Table"]
BT_PK[("PK = ORDER#<id>")]:::table
BT_SK[("SK = <variant> META/LINE#/PAYMENT#")]:::table
BT_ATTRS["Attrs: user_id, status, placed_at, total_cents, ..."]:::proj
end
subgraph GSI1["GSI1: OrdersByUser (PK=user_id, SK=placed_at DESC)"]
G1_PK[("PK = USER#<id>")]:::index
G1_SK[("SK = <placed_at>#ORDER#<id>")]:::index
G1_PROJ["Projection: KEYS_ONLY or INCLUDE {status,total_cents}"]:::proj
end
subgraph GSI2["GSI2: OrdersByStatus (PK=status#yyyy-mm, SK=placed_at DESC)"]
G2_PK[("PK = STATUS#<status>#<yyyy-mm>")]:::index
G2_SK[("SK = <placed_at>#<shard>#ORDER#<id>")]:::index
G2_PROJ["Projection: INCLUDE {user_id,total_cents}"]:::proj
end
A5 WRITE SHARDING
flowchart TB
%% A5 Write Sharding: ORDER_STATUS#<status>#<yyyy-mm>#<shard>
classDef idx fill:#f5f3ff,stroke:#8b5cf6,rx:6,ry:6;
classDef step fill:#ffffff,stroke:#94a3b8,rx:6,ry:6;
classDef note fill:#ecfeff,stroke:#06b6d4,rx:6,ry:6;
subgraph Client["Client / Order Service"]
direction TB
s1["On order status change"]:::step
s2["Compute shard = hash(order_id) % N"]:::step
s3["Compose GSI2PK = STATUS#<status>#<yyyy-mm>"]:::step
s4["Compose GSI2SK = <placed_at>#<shard>#ORDER#<id>"]:::step
end
subgraph DDB["DynamoDB Index: GSI2 OrdersByStatus"]
direction TB
pk1[("PK = STATUS#Shipped#2025-11")]:::idx
skex["SK = <placed_at>#<shard>#ORDER#<id>"]:::idx
end
q1(["Backoffice Query: \n PK=STATUS#<status>#<yyyy-mm> \n order by SK DESC \n page by Limit"]):::note
s1 --> s2 --> s3 --> s4 --> pk1
q1 --> pk1
A6 STREAMS PIPELINE
flowchart LR
%% A6 Streams Pipeline: DDB → Lambda → Payments/Shipments/Reporting
classDef ddb fill:#ecfdf5,stroke:#10b981,rx:6,ry:6;
classDef lam fill:#f0f9ff,stroke:#0ea5e9,rx:6,ry:6;
classDef sink fill:#fff7ed,stroke:#fb923c,rx:6,ry:6;
classDef guard fill:#fef2f2,stroke:#ef4444,rx:6,ry:6;
T["Orders Table mutations"]:::ddb --> S["DynamoDB Streams"]:::ddb --> L["Lambda Consumer (Idempotent)"]:::lam
L --> G1["Payments Service"]
L --> G2["Shipments Service"]
L --> G3["Reporting (S3/Parquet via Firehose)"]
L -. on failure .-> DLQ["DLQ"]:::guard
A7 IDEMPOTENCY SEQUENCE
sequenceDiagram
autonumber
participant C as Client
participant API as Orders API
participant DDB as DynamoDB
participant STR as Streams
participant L as Lambda
participant PAY as PaymentSvc
participant INV as InventorySvc
participant DLQ as DLQ
C->>API: POST /orders/{id}/authorize-payment (Idempotency-Key)
API->>DDB: PutItem PAYMENT#<ts> (Condition: not exists)
alt First attempt
DDB-->>API: OK
API-->>C: 200 Accepted
DDB-->>STR: INSERT
STR-->>L: Batch event
par Side-effects
L->>PAY: Authorize capture (idempotent token)
L->>INV: Reserve inventory (Condition: available >= :q)
end
else Duplicate
DDB-->>API: ConditionalCheckFailed
API-->>C: 200 (idempotent reuse)
end
L -. failure .-> DLQ: poison-pill isolation
November 5, 2025
November 5, 2025