Understanding Code Architecture: Clean, Hexagonal, Onion, COLA and a Go Implementation Guide
This article explains why code architecture matters, compares Clean, Hexagonal, Onion and COLA patterns, describes their layered structures and separation‑of‑concerns principles, and provides a practical Go project layout with concrete code examples to illustrate how each layer interacts.
The article emphasizes the importance of a well‑defined code architecture to avoid tangled, hard‑to‑maintain projects and to achieve separation of concerns, UI‑independence, framework‑independence, external‑component‑independence, and testability.
It introduces three classic architectural styles—Clean Architecture, Onion Architecture, and Hexagonal (Ports and Adapters) Architecture—showing their concentric‑layer diagrams and the common dependency rule that outer layers may depend on inner layers but not vice‑versa.
In Clean Architecture, the innermost Entities represent core business rules, surrounded by Use Cases (application business logic), then Interface Adapters (controllers, presenters, gateways), and finally the outer Frameworks & Drivers layer (UI, databases, web servers). Example Go entity definitions:
type Blog struct { ... } type Comment struct { ... }The Use Cases are expressed as interfaces such as:
type BlogManager interface { CreateBlog(...) ... LeaveComment(...) ... }Onion Architecture mirrors this structure with Domain Model , Domain Services , Application Services , and outer Infrastructure layers.
Hexagonal Architecture focuses on Ports (abstract interfaces) and Adapters (technical implementations), enforcing dependency injection from the outside in.
The article then presents the COLA (Clean Object‑oriented and Layered Architecture) pattern, which adapts the same principles to a four‑layer structure (Domain, Application, Interface, Infrastructure) and is widely used in Chinese projects.
Finally, a concrete Go project skeleton is proposed, illustrating how each layer maps to directories:
├── adapter // adapters for Gin, tRPC, Echo, etc. ├── application // business logic independent of frameworks │ ├── consumer // optional message consumers │ ├── dto // data transfer objects │ ├── executor // command/query handlers │ └── scheduler // optional cron jobs ├── domain // core business entities and rules │ ├── gateway // interfaces for infrastructure │ └── model // domain models ├── infrastructure // external dependencies and implementations │ ├── cache // Redis, Memcached, etc. │ ├── client // Kafka, MySQL, Redis clients │ ├── config // configuration parsing │ ├── database // persistence implementations │ ├── distlock // distributed lock implementations │ ├── log // logging wrapper │ ├── mq // message queue implementations │ ├── node // service discovery/coordination │ └── rpc // external service clients └── pkg // shared utilitiesSample adapter code using Gin demonstrates that framework‑specific code stays in the adapter layer:
import ( "mybusiness.com/blog-api/application/executor" // App layer "github.com/gin-gonic/gin" ) func NewRouter(...) (*gin.Engine, error) { r := gin.Default() r.GET("/blog/:blog_id", getBlog) ... } func getBlog(...) { // b is *executor.BlogOperator result := b.GetBlog(blogID) c.JSON(..., result) }The application layer executor implements business use cases while depending only on domain interfaces:
type BlogOperator struct { blogManager gateway.BlogManager // injected implementation } func (b *BlogOperator) GetBlog(...) { blog, err := b.blogManager.Load(ctx, blogID) return dto.BlogFromModel(...) }The domain layer defines the core BlogManager interface, and the infrastructure layer provides a MySQL implementation that converts data objects to domain models:
type MySQLPersistence struct { client client.SQLClient } func (p ...) Load(...) { record := p.client.FindOne(...) return record.ToModel() }In conclusion, the article advises introducing such architectures only for projects with longer lifespans and multiple maintainers, as they add complexity but greatly improve maintainability and testability.
Architect
Professional architect sharing high‑quality architecture insights. Topics include high‑availability, high‑performance, high‑stability architectures, big data, machine learning, Java, system and distributed architecture, AI, and practical large‑scale architecture case studies. Open to ideas‑driven architects who enjoy sharing and learning.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.