Data ownership
Give each service its own database, avoid the shared-database trap, and query across services with composition and replicated data.
- Explain database-per-service and why a shared database couples services
- Answer cross-service queries with API composition or locally replicated data
- Accept eventual consistency and decide where it’s acceptable
The quickest way to fake microservices is to split the code but keep one shared database. Then any service can read and write any table, a schema change breaks services you didn’t know existed, and you can’t deploy one service without checking all the others. You’ve built a distributed monolith.
The rule is database per service: each service owns its data and is the only one that reads or writes it directly. Everyone else goes through its API or listens to its events. Owning data also lets each service pick the right store - a relational database for Payments, a document store for the Menu, a time-series database for rocket telemetry.
Try it
Is this data access OK?
With database-per-service, which of these are fine and which break the rule?
“Orders calls the Menu API to get today’s prices”
“Kitchen runs SELECT * FROM menu_items on the Menu database”
“Delivery keeps its own copy of order addresses, updated from OrderPlaced events”
“Payments writes to the orders table to mark an order paid”
“A nightly analytics job copies every service’s data to a data warehouse”
“Two services share one “customers” table “to save time””
Querying across services
Without a shared database, “show my orders with dish names and delivery status” spans three services. Two patterns:
- API composition: a composer (often the gateway or a backend-for-frontend) calls each service and joins the results in memory. Simple, but slow if it fans out widely, and it must handle a service being down - show partial results rather than nothing.
- Replicated read data: a service keeps a local, read-only copy of the data it needs, updated from other services’ events. Fast and resilient, but the copy is eventually consistent: briefly out of date after a change.
Eventual consistency sounds scary, but it’s everywhere in real life: the bank shows a pending transaction before it settles. Ask the business where a few seconds of staleness is fine (menu descriptions) and where it isn’t (charging a card twice).
1orders = [{"id": 1, "customer_id": 7, "dish": "spicy miso"}, {"id": 2, "customer_id": 9, "dish": "tonkotsu"}]
2customers = {7: {"name": "Zara", "planet": "Mars"}} # the customer service doesn't know 9
3deliveries = {1: "in orbit"} # delivery has no record for order 2 yet
4
5for order in orders:
6 customer = customers.get(order["customer_id"], {"name": "unknown customer", "planet": "?"})
7 status = deliveries.get(order["id"], "status unavailable")
8 print(f"#{order['id']} {order['dish']} for {customer['name']} on {customer['planet']}: {status}")#1 spicy miso for Zara on Mars: in orbit #2 tonkotsu for unknown customer on ?: status unavailable
Key takeaways
Each service owns its data; others use its API or events, never its tables.
A shared database couples services through the schema - a distributed monolith.
Answer cross-service queries with API composition or event-fed local copies.
Copies are eventually consistent; decide with the business where that’s acceptable.
Lesson quiz
7 questions · pass with 5 correct · up to 50 XP
Passing this quiz completes the lesson and keeps your streak going. Questions you miss come back in review sessions later.
Practice: simulate microservice patterns in Python
Build small Python simulations of the patterns - routers, sagas, outboxes, circuit breakers, traces - and run them against sample inputs. They run locally in your browser; no servers or containers needed.
Find the shared tables
Each input line is service reads|writes table. A table should be written by exactly one service (its owner), and other services shouldn’t read it directly. For each table, alphabetically, print table: owner SERVICE when one service writes it and no others touch it, table: SHARED WRITES by a, b when several services write it, or table: owner SERVICE, direct reads by a, b when others read it directly. Finally print violations: N (tables that aren’t cleanly owned).
- NoodleMonolith’s database
Python runs in a sandboxed browser worker with a 60 second time limit. Its runtime loads from the Pyodide CDN; your code stays in this browser.
Compose the order view
The input has three JSON lines: a list of orders {"id", "customer_id", "dishes": [dish ids]}, the Customer service’s response (a dict from customer id to name, or null if the service is down), and the Menu service’s response (a dict from dish id to name). Print each order as #1 Zara: spicy miso, gyoza. Use unknown customer for a missing customer, customer unavailable when the service is down, and dish 42? for an unknown dish. End with complete or partial (if anything was missing or down).
- All present
- Customer service down
Python runs in a sandboxed browser worker with a 60 second time limit. Its runtime loads from the Pyodide CDN; your code stays in this browser.
Questions about this lesson
Stuck? Ask. Figured something out? Share it. Explaining is one of the best ways to learn.
Loading posts…