Design patterns

You MUST learn the proven single table design patterns & practices to design applications that full take advantage of DynamoDB scale/performance and is extensible in the long run.

This is just list of patterns to give you an astraunauts view of patterns landscape. In the course we will be diving deep into each one of these patterns and apply it to build a retail-banking model for ACME Bank

Entity design patterns

Pattern# 1 Add an attribute that holds the entity type

The application code refers to this attribute to interpret the item-type.

Pattern# 2 Entity type implied by Partition/Sort Key

Instead of using a separate attribute use a partition key (and sort key) naming pattern that identifies the item type. E.g.,

  • ORDER#112 identifies an order that has order-number=112
  • CUST#johndoe identifies a customer with name = johndoe

Pattern# 3 Attribute grouping in complex type

Keeps related attributes together. But keep in mind that attributes in the complex-type are not available for defining indexes. Example single attribute for customer-address in a Document type will reduce the number of attributes in your table but this would be a bad idea if an index needs to be created on let’s stay customer’s state of residence.

Primary key patterns

Best Practice# 1 Always use Composite Key

This is ensure that your table doesn’t need to be recreated if/when you have to add new entities to the table. The obvious question you may ask is - what do I store in the SK as I don’t need it for my known access patterns (today)

Best Practice# 2 Use Generic names for Partion & Sort keys

This is so that you can support multiple entities. E.g., if you were to manage Order and Customer in the same table - would you name Partition Key = OrderNumber or CustomerNumber?

  • It is common to name the key schema as {PK, SK}
  • Example {PK=ORDER#123} {PK=CUST#123}

Pattern# 1 Pre-Join related data

This is how DynamoDB gives you GREAT performance. Example is to maintain the order & order-item in the table with common partition key {PK=ORDER#123}

Pattern# 2 Composite sort key for hierarchical data queries

This is enable rich queries against the Sort Key.

Pattern# 3 Manage item/data versions

One-to-Many Relationship

Pattern #1 Complex attribute with collection of items

A customer can have multiple addresses. You may setup this relationship by managing addresses in a attribute such as a Set or Document. The downside is that you won’t be able to query on the reverse relationship.

Pattern #2 Use composite sort key for hierarchical one-to-many relationship

Pattern #3 Duplicate item data across entities

This is also referred to as Denormalization pattern. This is done to achieve pre-joining of data that in turn will alleviate the need to use additional queries.

Pattern #4 Use embedding with secondary index

Secondary Indexes

Pattern #1 Inverted index for reverse relationship

Pattern #2 Reloaded index key schema

Pattern #3 Sparse indexes

Many-to-Many Relationship

Pattern #1 Adjacency List

Adjacency list is an ordered list used to represent a graph. Each vertex in the graph is associated with the collection of its neighboring vertices or edges.

Write sharding patterns

Primarily used for preventing Hot Partition issue.

In DynamoDB, a partition key that doesn’t have a high cardinality can result in many requests targeting only a few partitions and resulting in a hot partition. A hot partition can cause throttling if the partition limits of 3000 RCU or 1000 WCU (or a combination of both) per second are exceeded.

Pattern #1 Natural Key Adjustment

Suggests using attribute data for adjusting the PK to increase the cardinality.

Pattern #2 Calculated suffix

Suggests making an additional attribute part of the partition key. E.g., customer number and email, the PK can be a combination of customer-number and calculated hash of email.

Pattern #3 Fixed prefix for shard#

Suggests using a prefix for the PK - this prefix identifies the shard. E.g., PK = SHARD#1#123, SHARD#2#234 …

Pattern #4 Random number suffix

Suggests using a random number suffix for the PK to increase the cardinality. E.g., PK = STATE_1, STATE_2, STATE_3 …

Scan patterns

Best Practice #1 Whenever possible

Best Practice #2 Use sparse GSI with projections if/when possible

Pattern #1 Use Parallel Scan* for better performance

Pattern #2 Use Stream + Offline analytics over scan

Transaction patterns

Best Practice #1 Use transactions ONLY if needed

Pattern #1 Add a sequential counter using transaction write

Stream patterns

Pattern #1 Build your aggregations using Stream

Pattern #2 Manage archival using Stream + TTL

Global patterns

Pattern #1 Use global table for multi master & local read performance