Skip to content
← All patterns

commerce

Customer Orders

What did this customer order?

A customer's order history, newest first, in one partition.

The canonical one-to-many collection. Orders share their customer's partition and sort by the date they were placed, so a customer's whole history is one Query and the recent end of it is the cheap end.

The model

Order

One order, sorted by the date it was placed.

pk
CUSTOMER#<customerId>
sk
ORDER#<placedOn>#<orderId>

Attributes: total (N), status (S), placedAt (N)

Customer

The customer record, at the head of its own collection.

pk
CUSTOMER#<customerId>
sk
CUSTOMER#<customerId>

Attributes: name (S), email (S)

Access patterns

  • QueryRead a customer and their orders

    The customer record and their whole order history, in one read.

  • GetItemRead one order

    A single order, by customer and order key.

Design notes

The date goes in the sort key, not just in an attributewhy

placedAt is on the item too, but the ordering comes from the sort key. A date attribute can only be filtered - every order read, then most of them discarded - while a date in the sort key is part of the key condition, so the rows you skip are never read. That is the same distinction the filter-trap lesson makes, applied to a collection that grows forever.

Newest first is a read-time choicetrade-off

The rows are stored ascending. Production passes ScanIndexForward=false to walk the collection backwards, so the most recent orders arrive first and a Limit of 20 costs twenty reads rather than the customer's entire history. That direction has a cost of its own: the customer record sorts first, so a descending read reaches it last, and an account page wanting the person plus their newest orders takes a separate GetItem for the header. The console here reads the collection as stored; direction is a property of the request, not of the table.

The customer sits in its own collectionwhy

CUSTOMER#c-ada appears as both partition and sort key, so the customer record sorts before every ORDER# in the same partition. One Query returns the person and their history together, which is what an account page needs, and the same read serves a page that only wants the person by stopping at the first row.

Windowing is the design, not the demotrade-off

A real order page asks for a date range - between two ORDER# prefixes - or the most recent N. The resolver handles only the partition half of a key condition, so the console reads the whole collection instead. The technique is unchanged and it is what the sort key is for; this page just cannot show you the narrower read.

Taught in the course

customer-ordersDynamoDB workbench
Ready to run
Explore an access patternSelect to load & run

The customer record and their whole order history, in one read.

Request
Execute against the local Dynoxide engine
ReturnedFiltered outChanged
PK(pk)
SK(sk)
placedAt
status
total
email
name
CUSTOMER#c-ada
CUSTOMER#c-adaS
—
—
—
ada@example.comS
Ada OkaforS
CUSTOMER#c-ada
ORDER#2025-11-16#o-1180S
1763251200N
refundedS
76.25N
—
—
CUSTOMER#c-ada
ORDER#2026-06-18#o-3344S
1781740800N
deliveredS
12.5N
—
—
CUSTOMER#c-ada
ORDER#2026-09-01#o-4870S
1788220800N
deliveredS
129N
—
—
CUSTOMER#c-ada
ORDER#2026-09-20#o-5121S
1789862400N
shippedS
48.9N
—
—
CUSTOMER#c-raj
CUSTOMER#c-rajS
—
—
—
raj@example.comS
Raj PatelS
CUSTOMER#c-raj
ORDER#2026-07-26#o-4102S
1785024000N
deliveredS
19.99N
—
—
CUSTOMER#c-raj
ORDER#2026-09-16#o-5044S
1789516800N
shippedS
234N
—
—
Awaiting request
Your next query starts here.

Choose an access pattern above, or build your own request. See what comes back and what it costs.

Write transactions, streams, tags and TTL are among the operations this browser build leaves out. dynoxide's native build has them.