cirle 1

Close

Zaya Corinne

Follow Me

cirle 1

Hexagonal, Clean, Microservices: Stop Comparing Things That Live at Different Levels

CleanVsHexagonalVSMicrosWaterMark01

Why most architecture debates are confused before they even start

Most architecture debates in software teams are broken before they begin.

Should we use Hexagonal or Clean?
Should we move to microservices?
Isn’t Clean Architecture better than microservices?

These questions sound technical. They sound sophisticated. They even sound strategic.

They’re also mixing concepts that operate at completely different levels of the system.

It’s like asking:

What’s better: a well-designed engine, a reliable gearbox, or a highway system?

They don’t compete.
They solve different problems.
And confusing them leads to expensive architectural theater.

What’s actually happening in most of these debates is cargo cult engineering — copying patterns without understanding the forces they were meant to address:

  • testability
  • coupling
  • deployability
  • team autonomy
  • operational scalability

Without understanding the force, the pattern becomes decoration.

To make sense of Hexagonal, Clean, and Microservices, we need to stop treating them like stylistic options and start placing them where they belong:

At different levels of architectural decision-making.


The Thee Levels of Architecture Most Teams Never Separate

If you don’t separate these levels, every architecture discussion becomes noise.

🧩 Level 1: Code Architecture (inside a service)

This level answers:

How is the code structured internally so that business logic stays independent from technical details?

This is where patterns like:

  • Hexagonal Architecture (Ports & Adapters)
  • Clean Architecture
  • Onion Architecture

actually live.

These are not about deployment.
They are not about scaling traffic.
They are not about splitting systems.

They are about protecting the core of your system — the business rules — from being polluted by frameworks, databases, messaging systems, or delivery mechanisms.

At this level, the main concern is dependency direction.

Does your domain depend on your database?
Or does your database depend on abstractions defined by your domain?

If you get this wrong, everything else becomes harder:

  • testing
  • refactoring
  • replacing infrastructure
  • evolving the system over time

Hexagonal and Clean are both attempts to answer one core question:

How do we make sure our business logic survices changes in technology ?

That’s Level 1.

Still no microservices in sight.


🧱 Level 2: Application Architecture (Inside One Deployable Unit)

Now we zoom out slightly.

At this level, we’re still inside one deployable system — a single service, a monolith, or an application.

The question here is:

How do we structure the modules and domains inside the application ?

This is where we think about:

  • bounded contexts
  • feature-based modules
  • domain partitioning
  • internal APIs

You might build a modular monolith.
You might slice by business capabilities.
You might apply Hexagonal or Clean principles within each module.

But we are still talking about internal structure, not distributed systems.

Most teams skip this level entirely. They go from “big ball of mud” straight to “let’s do microservices,” which is like solving messy room organization by buying a second house.

If Level 1 protects your code boundaries, Level 2 protects your domain boundaries.

Still not microservices.


🌍 Level 3 — System Architecture (Between Deployable Units)

This is where Microservices actually live.

Microservices are not primarily a code architecture pattern.
They are a system and organizational architecture pattern.

They answer a completely different question:

How do we split a system into independently deployable units that can be evolve separately, often owned by different teams ?

The drivers here are not just technical — they are organizational and operational:

  • Team autonomy
  • Independent deployment cycles
  • Independent scaling needs
  • Fault isolation
  • Ownership boundaries

Microservices introduce a new class of complexity:

  • networking
  • distributed tracing
  • observability
  • partial failures
  • eventual consistency

None of that is solved by Clean Architecture.
None of that is addressed by Hexagonal.

Because they were never meant to.


🧠 The First Big Insight

Hexagonal and Clean are about how code depends on other code.
Microservices are about how systems depend on other systems.

One is about dependency direction inside a process.
The other is about deployment and communication across processes.

They don’t compete.
They stack.

And when teams argue about which one to choose, what they’re really revealing is this:

They never separated the levels in the first place.


⏣ Hexagonal vs Clean: What’s Actually Different (and What’s Mostly Cosmetic)

Let’s clear this up first:

Hexagonal Architecture and Clean Architecture are not rival camps.
They are different expressions of the same underlying philosophy.

That philosophy is simple and powerful:

Business logic should not depend on technical details.
Technical details should depend on business abstractions.

Both Hexagonal and Clean are trying to enforce that rule. They just visualize and package it differently.


🔌 Hexagonal Architecture (Ports & Adapters)

Hexagonal Architecture, popularized by Alistair Cockburn, focuses on interaction boundaries.

The core idea is:

  • The application exposes ports (interfaces)
  • External systems connect through adapters
  • The domain stays isolated at the center

You don’t think in layers first.
You think in inside vs outside.

Anything that talks to the system from the outside world — UI, database, message broker, REST API — is an adapter. It plugs into a port defined by the application core.

This mental model is very pragmatic and interaction-focused.

It’s great for answering:

How do I prevent my application from being tightly coupled to a specific database, framework, or delivery mechanism?


🫧 Clean Architecture

Clean Architecture, popularized by Robert C. Martin, focuses more on dependency rules and concentric layers.

The visualization is different:

  • Entities and use cases at the center
  • Interface adapters around them
  • Frameworks and drivers at the outermost ring

The central rule is explicit:

Source code dependencies can only point inward

Frameworks don’t get to dictate your architecture. Your business rules don’t know or care about web servers, ORMs, or UI frameworks.

Clean is more prescriptive and layered in its presentation. It often leads to clearer separation of:

  • use cases
  • interface boundaries
  • delivery mechanisms

But it can also become overly ceremonial if applied dogmatically.


⚖️ The Practical Difference

In real-world systems, the difference often comes down to emphasis and style:

HexagonalClean
Primary focusInteraction boundariesDependency direction
Mental modelInside vs outsideConcentric layers
Typical strengthPragmatic decoupling from infrastructureStrong enforcement of architectural boundaries
Common failure modeToo loose, insufficient internal structureOver-engineered layering and indirection

But here’s the uncomfortable truth:

If your team understands dependency direction and isolates business logic, you’re already doing both

Most “Hexagonal vs Clean” debates are bikeshedding over diagrams and folder structures.

The real question is not: “Are we doing Clean or Hexagonal”.

The real question is:

Can we replace our database, framework, or delivery mechanism without rewriting our core business logic?

If the answer is no, you’re doing neither — regardless of how pretty your architecture diagram looks.


🎯 Why Microservices Don’t Replace Clean or Hexagonal

Now let’s talk about the most common architectural misunderstanding of the last decade.

Teams think:

We’re moving to microservices, so we don’t need Clean Architecture anymore

That’s how you end up with a distributed monolith.

Microservices solve system-level problems:

  • independent deployment
  • team autonomy
  • scaling boundaries
  • failure isolation

They do not solve:

  • poor dependency direction
  • tangled business logic
  • framework coupling
  • untestable core code

If you take a messy monolith and split it into 20 services, you don’t get better architecture.

You get 20 smaller messes… connected over the network.

Now your problems are:

  • harder to debug
  • harder to observe
  • harder to deploy
  • harder to reason about

And you’ve added distributed systems failure modes on top.

Microservices amplify whatever architecture you already have.

Good boundaries become powerful.
Bad boundaries become catastrophic.

That’s why internal architecture (Levels 1 and 2) must come before system architecture (Level 3).


⚠️ The Evolution Myth That Keeps Hurting Teams

There’s a dangerous narrative floating around:

Monolith → Clean → Hexagonal → Microservices

As if microservices were the natural “final stage” of architectural maturity.

They’re not.

The real evolution looks more like this:

Big ball of mud → Modular monolith → Clear domain boundaries → Strong internal architecture → Maybe microservices

Microservices are not a reward for architectural maturity.
They are a response to organizational and operational pressure.

You adopt microservices when:

  • teams are stepping on each other’s deployments
  • parts of the system need to scale independently
  • release cycles must diverge
  • ownership boundaries need to be enforced technically

Not because your codebase hit a certain number of lines.


📌How to Actually Design Architecture Without Cargo-Culting Patterns

If you remember only one thing from this article, make it this:

Architecture is not about choosing a diagram. It’s about responding to the dominant forces in your system.

Hexagonal, Clean, modular monoliths, microservices — these are tools. They solve different pressures at different stages.

The mistake is starting with:

We should use X architecture.

The right starting point is:

What kind of change, scale, and coordination pressure is this system going to face?

From there, architecture becomes a sequence of deliberate decisions, not a style preference.


Step 1: Protect The Core (Code Architecture First)

Before you think about services, clusters, or APIs, ask:

  • Can we test our business logic without spinning up half the system?
  • Can we change frameworks, databases, or delivery mechanisms without rewriting core rules?
  • Is the domain logic clearly separated from infrastructure concerns?

If not, your first job is internal architecture.

This is where Clean and Hexagonal principles matter:

  • Dependencies point inward
  • Infrastructure lives at the edges
  • Business rules are not coupled to tools

This is not over-engineering.
This is building software that survives change.

If your core logic is entangled with HTTP controllers, ORMs, and message brokers, microservices will not save you. They will just distribute the pain.


Step 2: Modularize Before You Distribute (Application Architecture)

Once your core is protected, the next question is:

“Are the different parts of this system clearly separated by business responsibility?”

Here you focus on modules and domains, not services.

You want:

  • clear bounded contexts
  • minimal cross-module leakage
  • explicit ownership of parts of the system
  • internal APIs between modules

This is the stage where you build a modular monolith.

It’s still one deployable unit.
But internally, it behaves like a well-organized system, not a pile of classes.

Most systems should live here longer than people think.

Because this is where you:

  • learn real domain boundaries
  • understand change patterns
  • discover where teams collide

Splitting too early freezes bad boundaries into network calls.


Step 3: Split Only When Organizational and Operational Pressure Demand It

Microservices should enter the picture only when you feel specific, measurable pain:

  • Teams constantly blocking each other’s deployments
  • Parts of the system needing very different scaling profiles
  • Independent release cycles becoming a competitive necessity
  • Failure in one area cascading across unrelated functionality
  • Ownership boundaries needing stronger enforcement

At that point, you don’t adopt microservices because it’s modern.

You adopt them because:

The cost of coordination inside one deployable unit is now higher than the cost of distributed complexity

That’s a trade-off.
Not a milestone.

And when you do split, the quality of your internal architecture determines whether you create:

  • clean, autonomous services
    or
  • a distributed monolith held together by fragile contracts (a.k.a. a microlith)

🤯 The Mental Model That Changes Everything

Stop thinking in terms of “which architecture should we use.”

Start thinking in terms of layers of decisions:

Decision LayerQuestion You’re AnsweringTypical Tools
Code ArchitectureHow do we protect business logic from technical churn?Clean, Hexagonal, Ports & Adapters
Application ArchitectureHow do we structure domains and responsibilities inside the system?Modular monolith, bounded contexts
System ArchitectureHow do we split systems for team and operational scalability?Microservices, service boundaries

When you see these as layers, something becomes obvious:

Microservices are not an upgrade to Clean or Hexagonal.

They are an aditional layer of decisions on top of them.


𖡎 Final Thought

Clean and Hexagonal help you build software that survives technical change.

Microservices help you scale teams and deployments around that software.

Confusing the two is how companies end up with elegant architecture diagrams… and systems that are fragile, slow to change, and painful to operate.

Good architecture isn’t about picking the most sophisticated pattern.

It’s about solving the right problem, at the right level, at the right time.

Comments (

0

)

Leave a Reply

Back to top

Discover more from The Scaling Mind

Subscribe now to keep reading and get access to the full archive.

Continue reading