Golang Clean Architecture
Audit Go services for Clean Architecture compliance. Ensures proper layering, dependency rules, and separation of concerns in gRPC → Usecase → Repository → Domain architectures.
When to Apply
Use this skill when:
- •Auditing service architecture
- •Reviewing new features for layer violations
- •Refactoring toward Clean Architecture
- •Code review for dependency rules
- •Planning service structure
- •Migrating to layered architecture
- •Ensuring testability through dependency injection
Architecture Layers
code
┌─────────────────────────────────────┐ │ Delivery (gRPC/HTTP/GraphQL) │ ← Thin, no business logic ├─────────────────────────────────────┤ │ Usecase (Business Logic) │ ← Orchestration ├─────────────────────────────────────┤ │ Repository (Data Access) │ ← CRUD only ├─────────────────────────────────────┤ │ Domain (Entities/Interfaces) │ ← Pure business logic └─────────────────────────────────────┘
Dependency Rule: Dependencies point INWARD only (toward domain).
Rules Covered (9 total)
High-Impact Patterns (4)
- •
high-business-logic-handler- Keep delivery layer thin - •
high-business-logic-repository- No business logic in data layer - •
high-constructor-creates-deps- Inject dependencies, don't create - •
high-transaction-in-repository- Transactions belong in usecase
Architecture Rules (5)
- •
arch-domain-import-infra- Domain must not import infrastructure - •
arch-concrete-dependency- Depend on interfaces, not concrete types - •
arch-repository-business-logic- Repositories do CRUD only - •
arch-usecase-orchestration- Usecases orchestrate, entities decide - •
arch-interface-segregation- Small, consumer-defined interfaces
Common Violations
❌ Business Logic in Handler
go
// gRPC handler doing calculations
func (h *Handler) CreateOrder(ctx context.Context, req *pb.Request) (*pb.Response, error) {
total := req.Price * req.Quantity // BAD: calculation in handler
discount := total * 0.1 // BAD: business rules in delivery layer
order := &domain.Order{
Total: total - discount,
}
return h.orderRepo.Save(ctx, order)
}
✅ Business Logic in Usecase
go
// Handler delegates to usecase
func (h *Handler) CreateOrder(ctx context.Context, req *pb.Request) (*pb.Response, error) {
order, err := h.orderUsecase.Create(ctx, req.Price, req.Quantity)
if err != nil {
return nil, err
}
return &pb.Response{OrderId: order.ID}, nil
}
// Usecase contains business logic
func (u *OrderUsecase) Create(ctx context.Context, price, quantity int) (*domain.Order, error) {
total := price * quantity // GOOD: calculation in usecase
discount := total * 0.1 // GOOD: business rules in usecase
order := &domain.Order{
Total: total - discount,
}
return u.orderRepo.Save(ctx, order)
}
❌ Repository with Business Logic
go
// Repository doing validation and business rules
func (r *OrderRepo) Save(ctx context.Context, order *domain.Order) error {
if order.Total < 0 { // BAD: validation in repository
return errors.New("invalid total")
}
if order.Total > 1000000 { // BAD: business rule in repository
order.Status = "needs_approval" // BAD: state change in repository
}
return r.db.Create(order)
}
✅ Repository Does CRUD Only
go
// Repository only handles data persistence
func (r *OrderRepo) Save(ctx context.Context, order *domain.Order) error {
return r.db.Create(order) // GOOD: simple CRUD
}
// Validation happens in usecase or domain entity
func (u *OrderUsecase) Create(ctx context.Context, price, quantity int) (*domain.Order, error) {
order := domain.NewOrder(price, quantity) // Entity validates itself
if err := order.Validate(); err != nil { // GOOD: validation in domain
return nil, err
}
if order.NeedsApproval() { // GOOD: business rule in domain
order.Status = "needs_approval"
}
return u.orderRepo.Save(ctx, order)
}
Trigger Phrases
This skill activates when you say:
- •"Audit architecture"
- •"Check layer dependencies"
- •"Review Clean Architecture"
- •"Verify separation of concerns"
- •"Check dependency rules"
- •"Review usecase/repository pattern"
- •"Check for layer violations"
- •"Audit service structure"
How to Use
For Architecture Audit
- •Identify all layers in the codebase
- •Check dependency directions (must point inward)
- •Verify each layer's responsibilities
- •Flag violations with specific rule references
For Code Review
- •Identify which layer the code belongs to
- •Check against layer-specific rules
- •Verify dependencies are injected, not created
- •Ensure interfaces are defined by consumers
Output Format
code
## Architecture Violations: X ### [Rule Name] (File: path/to/file.go) **Layer**: Delivery / Usecase / Repository / Domain **Issue**: Brief description of violation **Impact**: Tight coupling / Untestable / Wrong responsibility **Fix**: Suggested correction **Example**: ```go // Corrected code
Related Skills
- •golang-design-patterns - For refactoring large usecases
- •golang-idiomatic-go - For interface design patterns
- •golang-error-handling - For context propagation across layers
Philosophy
Based on Uncle Bob's Clean Architecture:
- •Independence - Business rules don't depend on frameworks, UI, or databases
- •Testability - Business logic can be tested without external dependencies
- •Flexibility - Easy to swap implementations (e.g., change database)
- •Maintainability - Clear boundaries make changes localized
Key Principle: The inner circles know nothing about the outer circles.
Notes
- •Rules enforce separation of concerns in Go services
- •Particularly focused on gRPC/usecase/repository pattern
- •Emphasizes dependency injection for testability
- •All examples follow Clean Architecture principles