Part 3 · 1 chapters · ~8 min

Data Ownership and Service Boundaries

One writer per piece of data, finding boundaries from business capabilities and change patterns, asking versus subscribing, read models, duplicated data and its staleness, cross-service queries (API composition, CQRS), and splitting a shared table.

6

Owners, queries and read models

code
// payouts keeps a read model of balances for display only
consumer.on('ledger.posting.created', async (e) => {
  await db.query(`INSERT INTO payouts.balance_view (account_id, balance_kobo, version) VALUES ($1,$2,$3)
                  ON CONFLICT (account_id) DO UPDATE SET balance_kobo = EXCLUDED.balance_kobo, version = EXCLUDED.version
                  WHERE payouts.balance_view.version < EXCLUDED.version`, [e.accountId, e.balanceKobo, e.version]);
});
// the payout decision does not use balance_view: it asks the ledger to place a hold
const hold = await ledger.createHold({ accountId, amountKobo, ref: payoutId }, { idempotencyKey: payoutId });

Finding boundaries: group what changes together and is owned by one team (business capabilities: ledger, payouts, identity, notifications). If two "services" must change in the same pull request most weeks, they are one service.

ONE OWNER PER PIECE OF DATA
others ask, subscribe or keep a read model
ledger serviceowns balances, postingspayouts serviceowns payoutsquery: GET /balances/:idevents: posting.createdread model in payoutscached balance view
swipe the figure sideways, or tap expand for full screen
1/4
ownership
Each table has exactly one owning service that may write it. Others never write it and ideally never read it directly.
one writer per tablethe boundary rule