Skip to content

Clean vs Layered Architecture: Which Go Architecture Should You Use?

Both Clean and Layered architecture are supported by verikt. They both enforce a top-down dependency direction, but Clean Architecture is more opinionated — it introduces entities, use cases, and a formal ring model, while layered gives you the same directional discipline with a simpler vocabulary.

CleanLayered
ComplexityHighMedium
Best forDDD, entity-centric domain modelingStandard CRUD APIs
Dependency rulesStrict (ring model)Strict (top-down)
Team sizeMedium / LargeAny
Directory structureComplexModerate

Clean Architecture structures a Go service around Uncle Bob’s concentric ring model. Entities are the innermost ring — pure business objects with no dependencies. Use cases orchestrate those entities. Interface adapters translate between use cases and the outside world. Infrastructure sits at the outermost ring.

cmd/<name>/ → Entry point
internal/entity/ → Core business entities (no dependencies)
internal/usecase/ → Application use cases
internal/interface/handler/ → HTTP handlers, adapters
internal/infrastructure/ → Database, external services, config
ComponentMay Depend On
entitynothing
usecaseentity
interfaceusecase, entity
infrastructureinterface, usecase, entity

The infrastructure package is explicitly the outermost layer — it’s allowed to depend on everything because it’s the wiring layer. Your entities and use cases never know about databases or HTTP.

Layered uses the classic three-tier model: handlers at the top, services in the middle, repositories at the bottom, with a shared model package for domain types.

cmd/<name>/ → Entry point
internal/handler/ → HTTP handlers (request/response)
internal/service/ → Business logic
internal/repository/ → Data access
internal/model/ → Shared types and domain models
ComponentMay Depend On
modelnothing
repositorymodel
servicerepository, model
handlerservice, model

Dependencies flow top-down. The model package is shared across all layers, which is both layered’s strength (simplicity) and its limitation (model changes affect everything).

  • Domain modeling: Clean Architecture has an explicit entity layer for core business objects. Layered uses model as a shared data structure layer — the distinction between domain logic and data types is less formal.
  • Use cases as first-class concepts: Clean Architecture treats each use case as a named, explicit concept (internal/usecase/create_order.go). Layered puts equivalent logic in service methods, which are less discoverable as a set of operations.
  • Infrastructure placement: In Clean Architecture, infrastructure is an explicit outer ring that wires everything. In layered, wiring is implicit — services import repositories directly.
  • Coupling: Layered’s shared model package creates implicit coupling across all layers. Clean Architecture’s entity layer is tighter but dependencies are more explicit.
  • Your project uses Domain-Driven Design and your team reasons in terms of entities and aggregate roots
  • Use cases are the primary way you communicate what the service does — each use case is a discrete, named operation
  • You’re building something where the distinction between domain logic (entities) and application orchestration (use cases) is meaningful
  • Your team knows Uncle Bob’s conventions and you want a structure that maps to that shared vocabulary
  • You’re building a standard HTTP API backed by a relational database
  • Your team is more familiar with the traditional handler → service → repository pattern
  • The business logic is about orchestrating data access and applying validation, not modeling a rich domain
  • You want a structure that any Go developer can navigate without prior explanation
  • Clean Architecture’s entity/usecase distinction feels like unnecessary ceremony for what you’re building
Terminal window
# Clean Architecture
verikt new my-service --language go --arch clean
# Layered
verikt new my-service --language go --arch layered