The Single Biggest Beginner Mistake with DynamoDB
Tags: aws, database
Published: 2022-04-03 19:18:51

[DynamoDB](https://aws.amazon.com/dynamodb/) is an amazingly powerful and performant database, best known for its low latency and elastic scaling characteristics. But there is one trap it is super easy to fall into, _especially_ if you have any background at all in more traditional relational database systems (think MySQL, Postgres, Oracle, and SQL Server).

## That's Not Normal, Man!
In the relational database world, the dependable best practice is to [normalize](https://www.guru99.com/database-normalization.html) your data model. It's a fairly academic topic, but the short version is that every piece of data should have _one_ home (i.e., one table which is its canonical location), and any references to that data in another table will be in the form of a foreign key, which a pointer to its "true" location.

In this way of storing data, an entity can be assembled from all the different rows in all the different tables by asking for a `JOIN` operation.

If you've ever done a data modeling exercise in this paradigm, you might have started with a table-per-entity. It might _feel_ natural to follow this same process with DynamoDB. Stop!

## Don't Be a Joiner
If you have taken DynamoDB for a spin, you may have noticed that there aren't any `JOIN` operations. This is a feature, not a bug! Let's talk a bit about why `JOIN` was invented in the first place. In the dawn of SQL databases (let's go back to 1979), storage was scarce and _expensive_. A database join saves storage at the expense of computation, a tradeoff which made sense for a long time, but doesn't anymore. In the present day, the cost equation is completely flipped: storage is millions of times cheaper, and while computation has also improved (a lot), not by the same orders of magnitude as storage, which means computation is now the bottleneck for cost and performance. DynamoDB achieves remarkable performance by not incurring the computation cost of doing all those joins.

If you make this common mistake (and I did, a bunch!) and continue modeling entities the old way, entity-per-table, you will end up doing all the joins yourself, in your application code. This is the worst of both worlds, because you're giving up the expressive flexibility of SQL while _still_ paying the cost of joins.

## So Now What?
What do you do instead? This problem is solvable with a little planning up-front. The best way I know to describe the new way of working is to imagine the query result you want, and store the rows in _that_ form, completely denormalized. There's an old wisdom about performance: the less it has to do, the faster it can be. With denormalized rows in your DynamoDB table, the only work is fetching by your chosen partition key (and optionally a _little bit_ extra to apply attribute constraints before returning).

## The Next Level
When you start modeling in this way, the surprising result is that most applications can be implemented _with a single database table_. This is like waking up from living in the Matrix. When you're ready for more, start with [this post](https://www.alexdebrie.com/posts/dynamodb-single-table/) by Alex Debrie, and then read everything else he has written! Absorb all of [this documentation](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/best-practices.html). And when you're done with that and you're ready to level up again, go find everything by Rick Houlihan, like [this talk](https://youtu.be/MF9a1UNOAQo) from re:Invent entitled "Amazon DynamoDB advanced design patterns."

## Go With the Flow
When you start using DynamoDB the way it was designed, it will blow your mind. Have fun on the journey!