Hexagonal Architecture와 Clean Architecture, 무엇이 같고 무엇이 다를까
목차
- Hexagonal Architecture란
- Clean Architecture란
- 둘이 공유하는 것
- 그렇다면 차이는 무엇일까
- 같은 시스템, 두 개의 관점
- 실제 프로젝트에서는 함께 쓴다
- Layer냐 Feature냐는 또 다른 축
- 그래서 무엇을 선택해야 할까
- 마무리
Clean Architecture를 공부하다 보면 Hexagonal Architecture라는 이름을 자연스럽게 만나게 됩니다. 그리고 처음에는 조금 혼란스럽습니다. 둘 다 도메인을 중심에 두고, 외부 기술에 대한 의존을 줄이고, 의존성 역전을 이야기하기 때문입니다. 그렇다면 둘은 같은 것일까요? 결론부터 말하면 비슷한 철학을 공유하지만, 바라보는 방향이 다릅니다.
Hexagonal Architecture란
Hexagonal Architecture는 Alistair Cockburn이 제안한 패턴으로, Ports and Adapters Architecture라고도 부릅니다. 핵심 아이디어는 한 문장으로 요약됩니다.
애플리케이션의 핵심 로직을 외부 세계로부터 분리하자.
애플리케이션의 한가운데에는 비즈니스 로직이 있습니다. 그리고 그 바깥에는 REST API, 메시지 큐, 데이터베이스 같은 다양한 외부 시스템이 있습니다. 이 둘 사이에 Port를 두고, Adapter로 연결합니다.
Port
Port는 애플리케이션과 외부 세계 사이의 접점(interface)입니다. 주문을 저장하는 기능이 있다고 해보겠습니다.
public interface IOrderRepository
{
Task Save(Order order);
}
이 인터페이스가 Outbound Port의 역할을 합니다. 애플리케이션은 저장소가 SQL Server인지 PostgreSQL인지 알
필요가 없습니다. 그저 IOrderRepository라는 Port를 통해 주문을 저장하면 됩니다.
Adapter
Adapter는 실제 기술과 Port를 연결합니다.
public class SqlOrderRepository : IOrderRepository
{
public async Task Save(Order order)
{
// SQL Server에 저장
}
}
나중에 MongoDB로 옮기더라도 Domain이나 Application 코드는 그대로 둘 수 있습니다. 구현이 하나 더 늘어날 뿐, Port를 바라보는 쪽은 변하지 않습니다.
Application
│
▼
IOrderRepository
▲ ▲
│ │
SqlAdapter MongoAdapter
│ │
SQL Server MongoDB
이것이 Hexagonal Architecture가 강조하는 전부입니다. 외부 세계와 애플리케이션 사이에 Port를 두고, Adapter를 통해 연결한다.
Clean Architecture란
Clean Architecture는 Robert C. Martin이 정리한 아키텍처입니다. 핵심은 Dependency Rule 하나로 압축됩니다.
소스 코드의 의존성은 항상 안쪽을 향해야 한다.
보통 동심원으로 그립니다. 하지만 여기서 중요한 것은 원의 모양이 아니라 화살표의 방향입니다.
예를 들어 Domain Entity가 특정 ORM을 직접 참조해서는 안 됩니다.
❌ Domain ──────► Entity Framework
⭕ Entity Framework ──────► Domain
즉 Clean Architecture는 의존성의 방향을 통제하는 것에 가장 큰 의미를 둡니다.
둘이 공유하는 것
이름은 다르지만 두 아키텍처가 공유하는 지점은 생각보다 넓습니다.
도메인이 중심이다
둘 다 비즈니스 로직을 외부 기술보다 위에 둡니다. 데이터베이스가 바뀌어도 비즈니스 로직이 흔들려서는 안 되고, REST API가 GraphQL로 바뀌어도 핵심 규칙은 그대로여야 합니다.
기술이 비즈니스 로직을 결정하는 것이 아니라, 비즈니스 로직이 기술로부터 독립되어야 한다.
Dependency Inversion
두 아키텍처 모두 의존성 역전 원칙(DIP)을 도구로 씁니다. 나쁜 구조는 이렇게 생겼습니다.
OrderService ──► SqlOrderRepository ──► SQL Server
OrderService가 구체적인 SQL Repository를 직접 알고 있습니다. 데이터베이스 기술이 바뀌면
애플리케이션 코드까지 영향을 받습니다. 사이에 인터페이스를 하나 두면 화살표 하나가 뒤집힙니다.
public class OrderService
{
private readonly IOrderRepository repository;
public OrderService(IOrderRepository repository)
{
this.repository = repository;
}
}
실제 구현은 바깥에 있으면서도, 애플리케이션은 추상화에만 의존하게 됩니다.
Framework와 Database로부터의 독립
또 하나의 공통점은 프레임워크와 데이터베이스를 중심에 두지 않는다는 것입니다. 다음 코드를 보겠습니다.
public class OrderService
{
private readonly DbContext context; // 기술이 Application 안으로 들어왔습니다
public async Task CreateOrder(...)
{
// ...
}
}
Application Layer가 DbContext를 직접 쓰고 있습니다. 이 구조에서는 데이터베이스 기술이
애플리케이션 계층까지 침투합니다. 반면 추상화를 두면 이렇게 됩니다.
public class CreateOrderHandler
{
private readonly IOrderRepository repository;
public CreateOrderHandler(IOrderRepository repository)
{
this.repository = repository;
}
}
애플리케이션은 데이터가 어디에 저장되는지 알 필요가 없어집니다.
여기까지는 두 아키텍처가 거의 같은 말을 합니다. 실제로 코드만 보고 "이건 Hexagonal이고 저건 Clean"이라고 구분하기 어려운 경우가 많은 이유입니다.
그렇다면 차이는 무엇일까
여기서부터가 중요합니다. 둘은 비슷한 구조를 만들어낼 수 있지만 출발점이 다릅니다.
Hexagonal Architecture는 "외부와 어떻게 연결할 것인가"에 관심이 있습니다.
Clean Architecture는 "의존성을 어떻게 관리할 것인가"에 관심이 있습니다.
Hexagonal은 Port와 Adapter에 집중한다
Hexagonal Architecture에서 중요한 것은 경계(boundary)입니다. 애플리케이션의 안과 밖을 나누고 그 사이에 Port를 둡니다. 외부와 통신하는 방법이 늘어나면 Adapter를 하나 더 만들면 됩니다.
REST Controller ─┐
Message Consumer ─┼─► Application ─► Database Adapter
CLI Adapter ─┘
즉 이런 질문을 중심에 둡니다. 애플리케이션의 핵심을 외부의 변화로부터 어떻게 보호할 것인가.
Clean은 Dependency Rule에 집중한다
Clean Architecture는 어떤 코드가 어떤 코드를 참조해도 되는지, 그 방향 자체를 규칙으로 못박습니다.
Infrastructure ──────► Application ──────► Domain
즉 이런 질문을 중심에 둡니다. 어떤 코드가 어떤 코드에 의존할 수 있는가.
같은 시스템, 두 개의 관점
재미있는 점은 동일한 주문 시스템을 두 관점으로 모두 설명할 수 있다는 것입니다. 다음과 같은 시스템이 있다고 해보겠습니다.
Hexagonal 관점에서는 이렇게 설명합니다.
REST API와 Message Queue는 Adapter이고, 애플리케이션과 외부 시스템 사이에는 Port가 있다.
Clean Architecture 관점에서는 이렇게 설명합니다.
Database와 Framework에 대한 의존성은 바깥쪽에 있고, Domain과 Use Case는 외부 구현에 의존하지 않는다.
같은 코드를 서로 다른 관점으로 설명하고 있을 뿐입니다.
실제 프로젝트에서는 함께 쓴다
그래서 실무에서는 굳이 둘 중 하나를 고를 필요가 없습니다. 오히려 이런 모습이 흔합니다.
Clean Architecture는 의존성이 안쪽을 향한다고 말하고, Hexagonal Architecture는 외부 시스템과 Application 사이에 Adapter와 Port가 있다고 말합니다. 둘은 충돌하지 않습니다. 오히려 잘 어울립니다.
Layer냐 Feature냐는 또 다른 축
여기서 한 단계 더 생각해볼 것이 있습니다. 두 아키텍처를 이야기하다 보면 자연스럽게 이런 폴더 구조가 나옵니다.
Controllers/
Services/
Repositories/
Entities/
하지만 이것은 아키텍처의 원칙과 폴더 구조를 혼동한 것일 수 있습니다. 같은 원칙을 지키면서도 이런 구조를 만들 수 있기 때문입니다.
Features/
├── Orders/
│ ├── CreateOrder/
│ │ ├── Endpoint.cs
│ │ ├── Handler.cs
│ │ ├── Validator.cs
│ │ └── Repository.cs
│ │
│ ├── CancelOrder/
│ │ ├── Endpoint.cs
│ │ ├── Handler.cs
│ │ └── Repository.cs
│ │
│ └── PayOrder/
│ └── ...
│
└── Users/
└── ...
이 구조에서도 Clean Architecture의 Dependency Rule을 지킬 수 있고, Hexagonal Architecture의 Port와 Adapter 개념도 그대로 적용됩니다. Vertical Slice를 조금 더 자세히 다룬 글은 Clean Architecture의 Vertical Slice와 Horizontal Slice에 정리해 두었습니다.
정리하면 세 개념은 서로 대체 관계가 아닙니다. 애초에 다른 질문에 답하고 있기 때문입니다.
| 개념 | 핵심 질문 | 한 단어로 |
|---|---|---|
| Clean Architecture | 의존성은 어느 방향으로 향해야 하는가 | Dependency |
| Hexagonal Architecture | 외부 시스템과 어떻게 분리하고 연결할 것인가 | Boundary |
| Vertical Slice | 코드를 어떤 기능 단위로 구성할 것인가 | Feature |
이 세 가지를 서로 다른 축으로 놓고 보면 논쟁이 대부분 사라집니다. "Clean이냐 Hexagonal이냐"는 질문은 사실 "세로축이냐 가로축이냐"를 묻는 것과 비슷합니다.
그래서 무엇을 선택해야 할까
사실 이 질문 자체가 조금 어긋나 있습니다. "Clean과 Hexagonal 중 무엇을 쓸까"보다는 "우리 시스템에서 지금 아픈 곳이 어디인가"를 묻는 편이 낫습니다.
- 비즈니스 로직이 데이터베이스나 프레임워크에 강하게 붙어 있다 → Dependency Rule
- 외부 시스템이 많고 API·메시지 큐·저장소를 교체할 가능성이 있다 → Port & Adapter
- 기능이 늘어나면서 Service 하나가 계속 비대해진다 → Vertical Slice
그리고 세 가지를 동시에 적용해도 아무 문제가 없습니다.
반대로 아픈 곳이 없는데 먼저 구조부터 세우는 것은 대체로 손해입니다. 인터페이스와 폴더가 늘어나면 그만큼 읽어야 할 코드도 늘어납니다. 아키텍처는 지금 겪고 있는 변경의 고통에 대한 대응이지, 미래에 대한 보험으로는 생각보다 잘 작동하지 않습니다.
마무리
Hexagonal Architecture와 Clean Architecture는 완전히 다른 아키텍처라기보다, 비슷한 철학을 서로 다른 각도에서 바라보는 방법에 가깝습니다. 둘 모두 다음을 중요하게 생각합니다.
- 도메인을 중심에 둔다
- 비즈니스 로직을 기술적 세부사항으로부터 보호한다
- 의존성 역전을 적극적으로 활용한다
- 데이터베이스와 프레임워크의 변경이 핵심 로직에 미치는 영향을 최소화한다
다만 강조점이 다릅니다. Hexagonal은 "외부와의 경계를 어떻게 만들 것인가"를 묻고, Clean은 "의존성의 방향을 어떻게 통제할 것인가"를 묻습니다. 여기에 Vertical Slice를 더하면 "변경되는 기능을 어떻게 하나의 단위로 묶을 것인가"까지 다룰 수 있습니다.
결국 중요한 것은 특정 아키텍처의 이름을 그대로 옮겨 심는 일이 아닙니다. 비즈니스 로직을 외부 기술로부터 보호하고, 변경의 영향을 줄이고, 코드가 비즈니스의 구조를 자연스럽게 드러내도록 만드는 것. 이 아키텍처들이 공통적으로 향하고 있는 곳은 거기라고 생각합니다.
Table of Contents
- What Hexagonal Architecture is
- What Clean Architecture is
- What the two share
- So where do they differ?
- One system, two points of view
- Real projects use both
- Layer versus feature is a different axis
- So which one should you pick?
- Closing
Study Clean Architecture for a while and you will run into the name Hexagonal Architecture. At first it is a little confusing, because both put the domain at the centre, both reduce dependence on external technology, and both talk about inverting dependencies. So are they the same thing? The short answer: they share a philosophy, but they look at it from different directions.
What Hexagonal Architecture is
Hexagonal Architecture is a pattern proposed by Alistair Cockburn, also known as Ports and Adapters Architecture. The core idea fits in one sentence.
Separate the core logic of the application from the outside world.
At the centre of the application sits the business logic. Outside it sit various external systems: a REST API, a message queue, a database. Between the two you place a port, and you connect through an adapter.
Port
A port is the interface between the application and the outside world. Say we have a feature that saves an order.
public interface IOrderRepository
{
Task Save(Order order);
}
That interface acts as an outbound port. The application does not need to know whether the store is SQL
Server or PostgreSQL. It just saves the order through the IOrderRepository port.
Adapter
An adapter connects the port to the actual technology.
public class SqlOrderRepository : IOrderRepository
{
public async Task Save(Order order)
{
// save to SQL Server
}
}
If you later move to MongoDB, the domain and application code can stay exactly as it is. There is one more implementation; nothing that looks at the port changes.
Application
│
▼
IOrderRepository
▲ ▲
│ │
SqlAdapter MongoAdapter
│ │
SQL Server MongoDB
That is the whole of what Hexagonal Architecture emphasises. Put a port between the outside world and the application, and connect through an adapter.
What Clean Architecture is
Clean Architecture is the architecture set out by Robert C. Martin. Its core compresses into a single Dependency Rule.
Source code dependencies must always point inward.
It is usually drawn as concentric circles. But what matters here is not the shape of the circles, it is the direction of the arrow.
A domain entity, for example, must not reference a specific ORM directly.
❌ Domain ──────► Entity Framework
⭕ Entity Framework ──────► Domain
So Clean Architecture places its greatest weight on controlling the direction of dependencies.
What the two share
The names differ, but the overlap between the two is wider than it first appears.
The domain is the centre
Both place business logic above external technology. A change of database must not shake the business logic, and swapping a REST API for GraphQL must leave the core rules untouched.
Technology should not decide the business logic; the business logic should be independent of the technology.
Dependency inversion
Both architectures use the Dependency Inversion Principle as a tool. The bad structure looks like this.
OrderService ──► SqlOrderRepository ──► SQL Server
OrderService knows the concrete SQL repository directly. Change the database technology and the
application code is affected. Put an interface in between and one arrow flips.
public class OrderService
{
private readonly IOrderRepository repository;
public OrderService(IOrderRepository repository)
{
this.repository = repository;
}
}
The real implementation stays on the outside, while the application depends only on the abstraction.
Independence from frameworks and databases
The other thing they share is refusing to put the framework and the database at the centre. Look at this code.
public class OrderService
{
private readonly DbContext context; // the technology has come inside the application
public async Task CreateOrder(...)
{
// ...
}
}
The application layer is using DbContext directly. In this structure the database technology
seeps into the application layer. Put an abstraction in and it becomes this instead.
public class CreateOrderHandler
{
private readonly IOrderRepository repository;
public CreateOrderHandler(IOrderRepository repository)
{
this.repository = repository;
}
}
The application no longer needs to know where the data is stored.
Up to this point the two architectures say almost the same thing. That is why it is often hard to look at code alone and declare "this one is hexagonal and that one is clean".
So where do they differ?
This is the important part. They can produce similar structures, but they start from different places.
Hexagonal Architecture cares about "how do we connect to the outside?"
Clean Architecture cares about "how do we manage dependencies?"
Hexagonal focuses on ports and adapters
What matters in Hexagonal Architecture is the boundary. You separate the inside of the application from the outside and put a port in between. If there is another way to talk to the outside, you add another adapter.
REST Controller ─┐
Message Consumer ─┼─► Application ─► Database Adapter
CLI Adapter ─┘
In other words, it puts this question at the centre. How do we protect the core of the application from changes outside it?
Clean focuses on the Dependency Rule
Clean Architecture nails down, as a rule, which code is allowed to reference which.
Infrastructure ──────► Application ──────► Domain
In other words, it puts this question at the centre. Which code is allowed to depend on which?
One system, two points of view
The interesting part is that the same order system can be described from both angles. Suppose we have this system.
From the hexagonal point of view you would say:
REST API and Message Queue are adapters, and there are ports between the application and the external systems.
From the Clean Architecture point of view you would say:
Dependencies on the database and the framework live on the outside, and the domain and use cases do not depend on external implementations.
It is the same code, described from two different angles.
Real projects use both
Which is why, in practice, you do not have to choose. This is a far more common shape.
Clean Architecture says dependencies point inward, and Hexagonal Architecture says adapters and ports sit between the external systems and the application. They do not conflict. If anything, they fit together well.
Layer versus feature is a different axis
There is one more step worth taking. Talk about these two architectures long enough and this folder structure appears on its own.
Controllers/
Services/
Repositories/
Entities/
But that may be confusing the principle with the folder layout, because you can honour the same principles and still build this.
Features/
├── Orders/
│ ├── CreateOrder/
│ │ ├── Endpoint.cs
│ │ ├── Handler.cs
│ │ ├── Validator.cs
│ │ └── Repository.cs
│ │
│ ├── CancelOrder/
│ │ ├── Endpoint.cs
│ │ ├── Handler.cs
│ │ └── Repository.cs
│ │
│ └── PayOrder/
│ └── ...
│
└── Users/
└── ...
This structure can keep Clean Architecture's Dependency Rule, and Hexagonal Architecture's ports and adapters apply just as well. I covered vertical slices in more depth in Vertical and Horizontal Slices in Clean Architecture.
So the three concepts do not replace one another. They were answering different questions to begin with.
| Concept | Central question | In one word |
|---|---|---|
| Clean Architecture | Which way should dependencies point? | Dependency |
| Hexagonal Architecture | How do we separate from, and connect to, external systems? | Boundary |
| Vertical Slice | What unit of functionality do we organise code around? | Feature |
Put the three on separate axes and most of the argument disappears. Asking "Clean or Hexagonal?" is a bit like asking "vertical axis or horizontal axis?".
So which one should you pick?
The question itself is slightly off. Rather than "Clean or Hexagonal?", the better question is "where does our system actually hurt right now?"
- Business logic is welded to the database or the framework → Dependency Rule
- Many external systems, and the API, message queue or store may be swapped → ports and adapters
- Features keep piling up and one service keeps growing → vertical slices
And applying all three at once is perfectly fine.
Conversely, building structure before anything hurts is usually a net loss. More interfaces and more folders mean more code to read. Architecture is a response to the pain of change you are feeling now; as insurance against the future it works less well than people expect.
Closing
Hexagonal Architecture and Clean Architecture are less two different architectures than two ways of looking at a similar philosophy. Both care about the same things.
- Put the domain at the centre
- Protect business logic from technical detail
- Use dependency inversion deliberately
- Minimise the effect that database and framework changes have on core logic
What differs is the emphasis. Hexagonal asks "how do we build the boundary with the outside?" and Clean asks "how do we control the direction of dependencies?" Add vertical slices and you can also address "how do we bundle a changing feature into a single unit?"
In the end the point is not to transplant the name of a particular architecture. Protect the business logic from external technology, reduce the blast radius of change, and let the code reveal the shape of the business naturally. That, I think, is where all of these architectures are heading.