Hexagonal vs Layered Architecture: Which Go & TypeScript Architecture Should You Use?
Both Hexagonal and Layered are supported by verikt. Layered is the most familiar pattern for Go developers coming from web frameworks — handler, service, repository. Hexagonal is stricter, more explicit about integration points, and better at scaling with complexity.
At a Glance
Section titled “At a Glance”| Hexagonal | Layered | |
|---|---|---|
| Complexity | High | Medium |
| Best for | Complex domain, multiple transports | Standard CRUD APIs |
| Dependency rules | Strict (ports & adapters) | Strict (top-down) |
| Team size | Medium / Large | Any |
| Directory structure | Complex | Moderate |
Hexagonal Architecture
Section titled “Hexagonal Architecture”Hexagonal architecture puts your domain and ports at the center. Adapters — HTTP handlers, database clients, message producers — are external and plug in through interfaces defined in port/.
domain/ → Pure business logic, no external dependenciesport/ → Interfaces (inbound use cases, outbound repositories)service/ → Use case implementationsadapter/ → External integrations (HTTP, DB, messaging)config/ → Configuration loadingplatform/ → Cross-cutting concernsinternal/bootstrap/ → Dependency wiringcmd/<name>/ → Entry point| Component | May Depend On |
|---|---|
| domain | nothing |
| ports | domain |
| service | domain, ports |
| adapters | ports, domain |
The structure forces you to define, explicitly, what the domain needs from the outside world (outbound ports) and what operations it exposes (inbound ports). Adapters satisfy ports — they never talk directly to each other.
Layered Architecture
Section titled “Layered Architecture”Layered is the classic three-tier pattern: handlers receive requests and delegate to services, services contain business logic and delegate to repositories, repositories handle data access.
cmd/<name>/ → Entry pointinternal/handler/ → HTTP handlers (request/response)internal/service/ → Business logicinternal/repository/ → Data accessinternal/model/ → Shared types and domain models| Component | May Depend On |
|---|---|
| model | nothing |
| repository | model |
| service | repository, model |
| handler | service, model |
Dependencies flow top-down: handlers know about services, services know about repositories. model is the shared foundation. It’s predictable, easy to explain, and maps closely to how most Go developers already think about web services.
Key Differences
Section titled “Key Differences”- Abstraction of integrations: Hexagonal makes every external integration explicit through ports and named adapters. Layered treats repositories and handlers as natural layers without requiring an interface boundary between them.
- Transport flexibility: Hexagonal handles multiple inbound transports cleanly — a gRPC adapter and an HTTP adapter both satisfy the same inbound port. Layered is handler-centric and adding a second transport requires structural changes.
- Learning curve: Layered maps directly to how most web frameworks organize code. Hexagonal requires understanding ports vs adapters before it clicks.
- Testability: Both enforce dependency direction, but hexagonal’s explicit port interfaces make it easier to swap adapters for fakes in tests without any additional setup.
When to Choose Hexagonal
Section titled “When to Choose Hexagonal”- Your service has multiple inbound transports (HTTP, gRPC, Kafka) that share domain logic
- You want every external dependency to be declared as an explicit interface in
port/ - Your business logic is complex enough that strict isolation from infrastructure matters
- You’re building a service that will grow significantly in scope and want the structure to hold up
When to Choose Layered
Section titled “When to Choose Layered”- You’re building a standard HTTP API with a single data store
- Your team already thinks in handler → service → repository terms
- The business logic is straightforward — orchestrating data access, not modeling complex domain rules
- You want a structure that new team members can understand in five minutes
- Hexagonal feels like over-engineering for your service’s current and expected complexity
Scaffold Either with verikt
Section titled “Scaffold Either with verikt”# Hexagonalverikt new my-service --language go --arch hexagonal
# Layeredverikt new my-service --language go --arch layered