A simple JOIN that takes 8ms in the monolith becomes impossible across separate databases. Try the three real fixes — API composition, a CQRS read model, and the shared-schema escape hatch.
The question to answer:“Show each learner with the courses they’re enrolled in and the total they’ve paid.”
Needs data from 3 capabilities: Identity (users) · Enrollment (courses) · Payment (amounts).
One database — three tables
🗄️ educonnect_db
👤
users
🎟️
enrollments
💳
payments
SELECT u.name, c.title, SUM(p.amount) FROM users u JOIN enrollments e ON … JOIN payments p ON …
Result
Press “Run the JOIN” — it just works.
What just happened
▹In a monolith, “show each learner with their courses and total paid” is one SQL JOIN across three tables — 8ms, done.
▹With database-per-service those tables live in three separate databases, so a SQL JOIN is simply not possible. This is the trade-off you buy with data ownership.
▹Three real fixes: API composition (call each service, stitch — always fresh, slower), a CQRS read model (pre-joined via events — fast, eventually consistent), or a shared schema / modular monolith (real JOINs back, but coupled). Pick by your consistency and latency needs.