Skip to content

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#&lt;id&gt;<br>SK: &lt;ts&gt;#ORDER#&lt;id&gt;"]
        G2["GSI2 OrdersByStatus<br>PK: STATUS#&lt;status&gt;#&lt;yyyy-mm&gt;<br>SK: &lt;ts&gt;#&lt;shard&gt;#ORDER#&lt;id&gt;"]
        GP["GSI_PAY Pending<br>PK: PAYSTATUS#&lt;status&gt;<br>SK: &lt;attempted_at&gt;#ORDER#&lt;id&gt;"]
        GS["GSI_SHIP Ops slice<br>PK: SHIP#&lt;status&gt;#&lt;carrier&gt;#&lt;yyyy-mm&gt;<br>SK: &lt;updated_at&gt;#ORDER#&lt;id&gt;"]
        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