A design review proposes a system using service discovery, a message broker, CQRS, event sourcing and distributed locks for a workload expected to have a few thousand users. What would you simplify, and how would you justify it?
Show the full answer Hide the answer
What to simplify
Nearly all of it. For a few thousand users, a single well-structured application with one relational database handles the workload comfortably, and each pattern above solves a problem this system does not have.
- Service discovery — needed when services are dynamic and numerous. With one deployable, unnecessary.
- Message broker — for background work at this volume, a queue in the primary database is better: enqueue is part of the same transaction as the state change that caused it, which removes the dual-write problem entirely rather than requiring an outbox to mitigate it.
- CQRS — justified when a read requirement cannot be served from the write model after indexing, denormalisation and caching. Unlikely here, and it costs a projection pipeline, a staleness window and a new class of silent incorrectness.
- Event sourcing — justified when history is a product requirement. It also means every version of every event schema must remain interpretable forever, which is a permanent tax.
- Distributed locks — needed when work is distributed. With one deployable, a database transaction or a conditional update is correct and simpler.
How to justify the simpler design
By naming what each pattern costs and what evidence would justify it, rather than by arguing against complexity in general.
- "A broker adds a second stateful system to operate at 3 a.m., and introduces the dual-write problem. We would adopt it when we have multiple independent consumers of the same events, or a measured throughput requirement beyond the database."
- "CQRS adds a pipeline that can be silently wrong. We would adopt it when a specific read query cannot be served acceptably after indexes and caching — measured, not assumed."
Naming the trigger is what makes the argument constructive. It says "not now" rather than "not ever", which is both more accurate and far easier for a team to accept.
What to keep
Structure that is cheap and pays immediately: enforced module boundaries with dependency checks, an interface between business logic and infrastructure, idempotency on operations with irreversible effects, and observability with correlation identifiers. None of these is distributed-systems complexity, and all of them make the future decomposition — if it ever happens — far cheaper.
The framing
Every pattern is a solution with a stated context and stated consequences. Applied where the context does not hold, you get only the consequences. The question is never "is this a good pattern" but "do we have the problem it solves, and what evidence would tell us we do?"