Skip to content

Hexagonal vs Flat Architecture: Which Go & TypeScript Architecture Should You Use?

Both Hexagonal and Flat are supported by verikt. They represent opposite ends of the spectrum: flat is a single package with no imposed structure, hexagonal is a fully layered service with strict dependency rules. The choice comes down to whether the thing you’re building is a service or a tool.

HexagonalFlat
ComplexityHighLow
Best forProduction APIs, microservicesCLI tools, scripts, prototypes
Dependency rulesStrictNone
Team sizeMedium / LargeAny
Directory structureComplexSimple

Hexagonal architecture is designed for services that need to last — production APIs, microservices, anything with non-trivial domain logic and multiple integration points. The structure enforces that business logic stays clean, isolated, and testable regardless of what’s wired to it.

domain/ → Pure business logic, no external dependencies
port/ → Interfaces (inbound use cases, outbound repositories)
service/ → Use case implementations
adapter/ → External integrations (HTTP, DB, messaging)
config/ → Configuration loading
platform/ → Cross-cutting concerns
internal/bootstrap/ → Dependency wiring
cmd/<name>/ → Entry point
ComponentMay Depend On
domainnothing
portsdomain
servicedomain, ports
adaptersports, domain

Every external integration is an adapter. Every domain boundary is a port. verikt check enforces the rules in CI — if your domain starts importing from an adapter, it fails immediately.

Flat is a single-package structure: main.go and go.mod. No layers, no enforced dependencies, no prescribed organization.

main.go
go.mod

You can still add verikt capabilities to a flat project — http-api will add the HTTP handler code, postgres will add database code — but there’s no layered structure to place them in. Everything lives together.

Flat is intentionally minimal. It’s the right choice when the overhead of a layered structure would add friction without adding value.

  • Structure: Hexagonal has 7+ directories with defined responsibilities. Flat has two files.
  • Dependency enforcement: Hexagonal enforces strict import rules via verikt check. Flat has none.
  • Capability integration: Hexagonal places capabilities in their correct layer (adapters, platform, etc.). Flat places everything alongside main.go.
  • Growth path: Hexagonal is designed to stay maintainable as the codebase grows. Flat is not — it accumulates coupling over time.
  • Onboarding: Flat is immediately understandable. Hexagonal requires understanding the port/adapter model first.
  • You’re building a production API or microservice
  • The service has business logic beyond simple request routing
  • You expect the codebase to grow — more developers, more capabilities, more integrations
  • You want verikt check to enforce architecture boundaries in CI
  • Multiple inbound transports (HTTP + gRPC, HTTP + Kafka) sharing the same domain logic
  • You’re building a CLI tool, script, or one-off utility
  • It’s a prototype or proof of concept that may never reach production
  • The entire codebase will fit in a few hundred lines
  • You need to move fast without any structural overhead
  • There’s no meaningful separation between “domain logic” and “infrastructure” in what you’re building
Terminal window
# Hexagonal
verikt new my-service --language go --arch hexagonal
# Flat
verikt new my-tool --language go --arch flat