Let’s pull three commonly discussed software architectures out of their technological context and reduce them to an easily understandable analogy.
Imagine we split the functional actions of a Java application into people.
Each person represents a well-defined responsibility.
All of them speak the same language: Java.
For the sake of analogy, let’s call that language German.
Communication inside an application happens directly in German.
Communication between applications, via web services, requires switching to a different language—let’s say French—and flattening the message into text. That is an email. Emails convey intent but lose nuance, especially when translated and interpreted by another team.
Publish/subscribe communication is like posting messages to Slack in French: no specific recipient, asynchronous, and permanently archived. Each reader risks misinterpreting the meaning in their own way.
With that setup, we can now explain three architectural approaches purely in terms of how these people (“Germans”) are seated and allowed to communicate.
1. The Monolith
All Germans in one room, speaking German
Setup
All responsibilities are co-located. Germans can talk directly to each other, in their native language, synchronously. If German A needs something from German B, they simply talk.
There is no enforced separation by walls. Boundaries exist, but they are conceptual, not physical.
Advantages
-
Shared understanding
- Everyone operates in the same context.
-
Cheap integration
- Communication is direct and expressive.
-
Atomic business actions
- Multiple responsibilities can participate in a single coherent action.
-
Easy correction
- If a misunderstanding arises, it can be resolved immediately.
-
Single source of truth
- Data and meaning are unified.
Costs
-
Boundary discipline required
- Germans must respect who owns which responsibility.
-
Local mistakes can have global impact
- Poor decisions are visible immediately.
-
Scaling a single responsibility independently is hard
- You cannot easily “clone” one German without duplicating others.
Risks
-
Boundary erosion
- Responsibilities can blur if discipline weakens.
-
Accidental coupling
- One German may start depending on another’s internal work.
-
Local optimization
- Short-term fixes can damage the overall structure.
-
Organizational scaling limits
- Very large teams may struggle to operate in one shared context.
Key property:
Mistakes are visible, understandable, and cheap to correct.
2. Microservices
Each German in their own room, communicating via email in French
Setup
Each responsibility is isolated in its own room. Germans no longer speak German to each other. All communication happens via written emails in French—flat, structured text.
This enforces boundaries physically. Integration is explicit and asynchronous.
Advantages
-
Independent scaling
- One German can be replaced by ten if needed.
-
Failure isolation
- A failing German does not immediately stop others.
-
Clear ownership
- Each room has a well-defined responsibility.
Costs
-
Translation overhead
- Every interaction requires translating intent into French.
-
Loss of shared context
- Germans must guess intent from emails.
-
Higher coordination cost
- Simple changes require multiple conversations.
-
Infrastructure overhead
- Delivery, monitoring, retries, versioning.
Risks
-
Wrong division of responsibilities
- Poor splits become very expensive to undo.
-
Overlapping islands of responsibility
- Multiple Germans independently implement the same business logic.
-
Data duplication
- Each German keeps their own filing cabinet.
-
Loss of a single source of truth
- No authoritative version of reality exists.
-
Semantic divergence
- Shared data is interpreted differently in each room.
Key property:
The system keeps working—even when it is conceptually wrong.
3. Event-Driven Architecture
Separate rooms, communicating via Slack
Setup
Germans may be together or apart, but they do not talk directly. Instead, they post messages in Slack. Anyone interested may read and react—on their own time, in their own transaction.
No one addresses anyone directly. Communication is indirect and reactive.
Advantages
-
Loose coupling
- Germans do not know who reacts to their messages.
-
High scalability
- Many Germans can react independently.
-
Temporal decoupling
- Work can happen asynchronously.
Costs
-
Operational complexity
- Ordering, retries, duplicates, monitoring.
-
Mental overhead
- Understanding the system requires reconstructing conversations.
-
Delayed feedback
- Errors surface late.
Risks
-
No atomic guarantee of full business correctness
- No single place ensures all rules are satisfied.
-
Irreversible partial states
- Messages cannot be “unsent.”
-
Loss of causal clarity
- It is unclear who reacted to what, and why.
-
No easy end-to-end traceability
- Debugging becomes historical investigation.
-
Emergent workflows
- The real business process exists only implicitly.
Key property:
Correctness becomes an emergent property, not a guarantee.
Practical Example: Employee Termination
To ground the three architectural setups in reality, consider a common HR workflow.
An employee’s contract is terminated. As a result:
Physical access (badges, doors) must be revoked
Logical access (user accounts, credentials) must be disabled
Final compensation must be calculated and settled
Audit and compliance records must be updated
At first glance, these concerns appear loosely related. They involve different systems, different responsibilities, and different teams. This makes the scenario an attractive candidate for Microservices or Event-Driven Architecture.
However, this example is precisely where architectural trade-offs become visible.
The Essential Business Requirement
The core requirement is not that these steps happen “eventually”.
The requirement is:
A terminated employee must not retain access to company systems or premises, and termination must either succeed completely or fail explicitly with a known reason.
This implies several non-negotiable properties:
Partial success is unacceptable
Silent failure is unacceptable
Security and compliance violations must be prevented, not detected later
Responsibility for correctness must be unambiguous
In other words: failure must be both prevented and detectable.
Monolith (Single Transaction, Rich Domain)
In a monolithic setup with a rich domain model:
Termination is a single business operation
Access revocation, payroll settlement, and account deactivation are part of one transactional boundary
If any step fails, the operation fails
The failure reason is explicit and immediate
The system enforces the invariant:
“An employee cannot be terminated while retaining access.”
Failure is not inferred from logs or dashboards.
It is returned as a business outcome.
Cost profile:
Low cost of correction
High confidence in correctness
Clear ownership of invariants
Microservices (Distributed Responsibilities)
In a microservices setup:
HR publishes a “contract terminated” command or event
Identity, Facilities, Payroll, and Audit services act independently
Each service succeeds or fails in isolation
At this point, the system has lost a critical capability:
There is no single place that can know whether the business operation succeeded.
What does “failure” mean here?
One service is temporarily unavailable
Another retries later
A third succeeds immediately
The system may end up in a state where:
Payroll is settled
Access is still active
No component considers itself “in error”
Failure is no longer detectable, only suspected.
Cost profile:
High operational overhead
Expensive reconciliation logic
Human intervention becomes part of the control flow
Event-Driven Architecture (Asynchronous Propagation)
In an event-driven setup:
HR emits an
EmployeeTerminatedeventDownstream systems consume it asynchronously
No consumer reports completion back to the origin
This setup optimizes for decoupling and throughput, but introduces a fatal ambiguity:
Silence is indistinguishable from failure.
If access is not revoked:
Was the event not consumed?
Is the consumer delayed?
Did it crash after partially processing?
Will it retry?
There is no authoritative answer.
As a result:
Security risks can exist unnoticed
Compliance violations are discovered retrospectively
Correctness becomes a monitoring problem rather than a design property
Cost profile:
Low upfront coordination cost
Extremely high cost of being wrong
Correction happens after damage has occurred
The Hierarchy of Correctness
Prevention: The Monolith. It is physically impossible to be "half-terminated."
Detection: Microservices. You need a separate "Auditor German" to walk between rooms and check everyone's folders.
Hope: EDA. You post the message and hope everyone's Slack client is online.
Why This Example Matters
This scenario demonstrates a general rule:
Distributed architectures are only viable when failure is acceptable and reliably detectable.
In employee termination:
Failure is unacceptable
Failure must be immediately visible
Partial success creates irreversible risk
For that reason, both Microservices and Event-Driven Architecture are poor fits here—not because they are inherently flawed, but because they do not align with the essential business requirement.
To compensate, a distributed setup must introduce continuous coordination: tracking execution across multiple steps, detecting missing or delayed outcomes, retrying, reconciling state, and alerting on anomalies. Ensuring correctness becomes an ongoing operational concern rather than a property of the design itself.
Compared to the simplicity of a monolith—where correctness is enforced by construction—this represents a substantial increase in code, process, and cognitive overhead.
It is true that real-world employee termination often spans multiple systems. However, the example remains deliberately relatable, and the consequences of failure or delayed detection are immediately tangible. That clarity makes the cost of correction, and even the cost of detecting misfires, explicit rather than theoretical.
The Ideal Baseline
If we ignore technology and look purely at business structure, one conclusion emerges:
By definition, a monolith is always the best suited architecture - until the business requires that it is not
Why?
Because in a monolith:
All responsibilities share one language
All business rules can be enforced atomically
All meaning lives in one place
All mistakes are cheap to see and cheap to fix
If the system has:
One coherent domain
Shared business invariants
A need for correctness over time
Then keeping all Germans in one room is not a compromise — it is optimal.
So the question is not:
“Why use a monolith?”
The real question is:
“When does the ideal no longer apply?”
The Legitimate Exception: Disjoint Load Characteristics
There are scenarios where keeping everyone in one room stops being practical — not because of complexity, but because of scale asymmetry.
Consider this setup:
The business revolves around social media posts
-
But the operations on the posts are fundamentally different:
- Read post happens millions of times per second
- Write post happens far less frequently
- Delete post is rare and tightly controlled
In German terms:
One entire floor of Germans is answering read questions
A small team handles writes
An even smaller, highly trusted group handles deletions
These Germans do not need to talk much:
Reads do not change the world
Writes are controlled
Deletes are exceptional
Here, separating rooms makes sense — not because of domain complexity, but because the work is operationally disjoint.
This is the strongest legitimate case for splitting responsibilities.
Microservices Revisited (Narrowly)
Under this lens, microservices only truly pay off when:
Responsibilities are functionally isolated
Communication between them is rare and well-defined
Different responsibilities require radically different scaling
Temporary inconsistency is acceptable or irrelevant
In other words:
Microservices work best when the Germans almost never need to talk.
The moment frequent coordination is required, the cost curve turns sharply negative.
And Event Driven Architectures?
Using the same analogy, Event Driven Architectures would mean:
Germans do not even address each other directly
They post messages in Slack
Others may react
Correctness emerges eventually
And this is where the business case collapses.
Because in business terms:
There is no single German responsible for “done”
No one guarantees the full set of rules
No one can say: this action is complete and correct
Event Driven Architectures only makes sense if:
Partial correctness is acceptable
Business processes are tolerant to delay
Recovery is handled by compensation, not prevention
These are exceptional business cases, not normal ones.
Which is why, in the German analogy:
Event Driven Architectures optimizes for throughput, not for correctness.
If failure is expensive, regulated, or reputationally damaging, Event Driven Architectures increases risk rather than reducing it.
Now: The Cost of Correction (Reframed)
With the baseline and exceptions defined, we can now speak meaningfully about correction cost.
-
In the ideal monolith, correction is:
- Local
- Immediate
- Cheap
-
In microservices, correction is:
- Cross-room
- Coordinated
- Expensive
-
In Event Driven Architectures, correction is:
- Historical
- Indirect
- Often irreversible
This is not a technical drawback.
It is a business risk profile.
If the Monolith Is So Perfect, Why Does It Fail?
Despite its clear advantages in integration, correction, and simplicity, monoliths fail often in the real world. The reason is not the architecture itself—it is the ability of the team to enforce boundaries and model the domain correctly.
Even in a single room, mistakes can propagate if responsibilities are blurred, communication is sloppy, or the design does not reflect the business domain. These are the essential risks we outlined earlier:
Boundary erosion – responsibilities bleed into each other
Accidental coupling – one German depends on another’s internal work
Local optimization pressure – short-term fixes damage global consistency
Organizational scaling limits – large teams struggle to coordinate effectively
Procedural Programming (Anemic models) Exacerbate the Risks
In programming using anemic models:
Responsibilities are not naturally encapsulated
State flows freely and can be mutated anywhere
Ownership is implicit, not enforced
Translated to our analogy:
Germans walk around the room freely, touching everyone else’s filing cabinet
One German’s shortcut can silently break others’ work
Understanding the “current state” becomes difficult and expensive
As a result, the same essential risks (boundary erosion, accidental coupling, loss of integration) manifest more severely and more frequently.
Even in a monolith, procedural teams cannot reliably enforce boundaries. The architecture cannot protect them.
Microservices and Event-Driven Architectures in the Absence of Business Justification: Band-Aids for Missing Discipline
In many organizations, the apparent solution to growing complexity is to physically separate the Germans into different rooms (microservices), or to replace direct collaboration with asynchronous, message-based communication (event-driven architectures).
At first glance, this appears to work:
Overlapping responsibilities are reduced
Accidental coupling becomes less visible
Scaling by headcount seems easier
However, these benefits are largely short-lived.
The core problem remains unchanged:
the absence of strong, internal boundaries and clearly owned responsibilities.
The architecture does not solve this problem; it merely obscures it.
As a result, new and often more severe problems emerge:
Misaligned responsibilities become harder to detect and even harder to correct
Data is duplicated, diverges, or loses a single source of truth
Eventual consistency issues replace transactional guarantees
Operational overhead and cognitive load increase dramatically
In practice, the architecture introduces complexity that is unrelated to actual business needs. Microservices and EDA trade local simplicity for artificial isolation, producing a system that is more difficult to reason about, operate, and correct than the problem it set out to solve.
At a basic development level, we learn not to “handle” a NullPointerException, but to eliminate the conditions that make it possible. The same principle applies to architecture:
you do not fix missing discipline by distributing its consequences.
The Real Long-Term Solution: Enable the Monolith
If the business domain does not follow ‘The legitimate exception’ - the cheapest, safest, and most enduring solution is not to build walls, but to train the team to maintain strong boundaries internally:
Each German owns clear responsibilities
Interactions are explicit and meaningful
Business rules are modeled correctly in the code itself
This is the essence of Rich Domain Modeling:
Integration remains natural
Correction is cheap
Scaling the team or the system is easier because boundaries are conceptually enforced, not physically imposed
In other words: if your team can develop rich domain models, the monolith becomes not just viable—it is the easiest and cheapest and most maintainable long-term architecture.
Walls (microservices, EDA) are only justified when responsibilities are operationally disjoint, not because the code lacks discipline.
Top comments (0)