# One table or many: modeling across services

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](https://www.kanject.com/docs/dynostudio-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.

> **Pitfall:** A single shared table across services quietly **recouples the services you split apart**. Now one team's schema change can break another's reads; you can't scale or secure per service; and an independent deploy is no longer independent. The one-request-for-related-data win of single-table design assumes *one* service owns the data — across a service boundary, that assumption is gone.

## 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.)

> **One home per fact:** The rule that keeps this sane: every fact has exactly one **system of record**. Copies in other services are caches — useful, fast, allowed to lag — but the home is the truth, and writes go there. When two services disagree, the home wins.

## 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](https://www.kanject.com/docs/dynostudio-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**

- [Pattern: Database per service](https://microservices.io/patterns/data/database-per-service.html) — microservices.io (Chris Richardson): why each service owns its own database, and the tradeoffs.
- [Single-table vs. multi-table design in Amazon DynamoDB](https://aws.amazon.com/blogs/database/single-table-vs-multi-table-design-in-amazon-dynamodb/) — AWS Database Blog: the within-a-service layout tradeoff, in depth.

**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.

---
_Source: https://www.kanject.com/docs/dynostudio-multi-table-services/ · Kanject Docs_
