Competing Consumers
A single worker cannot keep up with a bursty or growing stream of background work, but processing that work inline in the request path blocks users and exposes the application to load spikes it has no way to shed.
A single worker cannot keep up with a bursty or growing stream of background work, but processing that work inline in the request path blocks users and exposes the application to load spikes it has no way to shed.
A single data model forced to serve both writes and reads eventually deforms under the pressure - write-side schemas grow denormalized columns for dashboards, read-side queries are slowed by indexes that only exist for constraint enforcement, and neither job gets done well.
Storing only the current state of an entity throws away the one thing a business often needs most - the history of how it got there. Audits become guesswork, "why did this happen?" becomes unanswerable, and rebuilding alternative projections of the data requires going back to the operational logs no one kept.
Deploying code to production is irreversible in the short term - when something goes wrong, rolling back requires another deploy, which takes time and may have its own risks.
Retrying a failed API request can trigger duplicate side effects - charging a card twice, creating two accounts, or sending the same email multiple times.
Events published directly inside a database transaction can be lost if the broker is unavailable, leaving the database and downstream consumers permanently out of sync.
A business transaction that spans multiple services cannot use a database transaction - there is no single database. Without coordination, partial failures leave the system in an inconsistent state with no automated recovery path.
Fetching the same data from a slow database on every request wastes latency and database capacity, but manually managing cache population across every call site is error-prone and hard to keep consistent.