One table or many: modeling across services

View .md

The single-table-vs-multi-table debate is usually flattened into one question. It is really two, and they are independent: how you lay out one service's data, and how you draw table boundaries between services. Confusing them is why the debate feels unresolvable — the honest answer to "one table or many?" is almost always both.

You'll learn
  • Separate the two independent decisions — data layout within a service vs table ownership across services
  • See why services keep a table per bounded context, and why one shared table recouples them
  • Handle data that spans a boundary — reference by id or a synced copy, with one clear system of record
  • Recognise that a mature system is usually both single- and multi-table

Two decisions, not one

Inside a single service, "one table or many" is the single-table design tradeoff you met in DynamoDB basics: co-locate related items under one key for fewer round-trips, or keep a table per entity for simpler shapes. That is a decision about access patterns.

Across services, the question is different — it is about who owns the data. And here the answer is settled: each service owns its own table(s). This is the database-per-service pattern, and it is not a DynamoDB quirk — it is how independent services stay independent, on any database.

A table per bounded context

Take an e-commerce backend split into services. Each service owns the data for its bounded context — its slice of the domain — and nobody reaches across:

text
Identity service   →   "Identity"   (users, credentials, sessions)Orders service     →   "Orders"     (orders, line items, shipments)Catalog service    →   "Products"   (SKUs, prices, inventory)Each service owns its table(s). No service reads another's table directly.

The Identity table is the login service's alone; the Orders table is the orders service's alone. Each can deploy on its own schedule, scale to its own load, and be locked down with its own IAM policy. Whether the orders service uses single-table design inside its Orders table is its own private call — it changes nothing for anyone else.

When data crosses a boundary

Of course the orders service still needs a buyer's name on the receipt. It does not read the Identity table to get it. Two honest options instead: reference by id — store the CustomerId on the order and ask the identity service when you need the details — or keep a small denormalized copy (name, email) in the Orders table, refreshed when identity changes. The buyer's home — the system of record — stays in Identity either way.

The tradeoff is the usual one. A reference is always fresh but adds a runtime dependency and a hop; a copy is fast and resilient but eventually consistent and needs a way to stay in sync — typically an event the owning service publishes and the consumer applies. (On a Kanject backend that is an event through the hub; the pattern is identical on any stack.)

Single-table and multi-table, together

So the two axes are orthogonal, and a real system lives on both at once. Across services: separate tables — Identity, Orders, Products. Within the orders service: maybe a single-table design that co-locates an order with its line items and shipments under one key, so the order page is one Query. Multi-table across, single-table within — and that is a perfectly coherent, common shape.

That is why Bean & Bark is single-table end to end: it is one store — one bounded context — so co-locating a customer with her orders is exactly right. Grow it into a platform with separate identity, catalog, and fulfilment services, and you'd have several tables across them, some single-table inside. Neither choice was ever wrong; they answer different questions.

Where DynoStudio fits

DynoStudio meets you where you are. Model one table or many; point a workspace at one service's table or several; the Model Builder holds a per-service model, and connections are scoped per stage so you can browse the orders service without touching identity. Single-table, multi-table, or both, the "Executes as" strip discloses the same cost for whichever design you chose — the studio respects the boundaries you drew rather than pushing a philosophy of its own.

Going deeper
Recap
  • "One table or many" is two independent decisions: data layout within a service, and table ownership across services.
  • Across services, each owns a table per bounded context — a shared table recouples independent services and forfeits per-service deploy, scale, and security.
  • Data that crosses a boundary has one system of record; other services reference it by id or keep a synced copy, never a second source of truth.
  • A mature system is usually both — multi-table across services, sometimes single-table within one. DynoStudio models whichever you chose.
Was this page helpful?