clean-architecture

Installation
SKILL.md

When to Use

  • Designing a new project structure from scratch
  • Refactoring existing code to clean architecture
  • Creating domain entities, use cases, or repositories
  • Defining boundaries between layers
  • Implementing dependency injection patterns

Core Principles

Principle Description
Dependency Rule Dependencies point inward. Inner layers know nothing about outer layers
Entities Enterprise business rules, independent of application
Use Cases Application business rules, orchestrate entities
Interface Adapters Convert data between use cases and external agents
Frameworks & Drivers External tools (DB, Web, UI) - outermost layer

Layer Structure

project/
├── domain/                 # Innermost - Enterprise Business Rules
│   ├── entities/           # Business objects with behavior
│   └── value_objects/      # Immutable domain primitives
├── application/            # Application Business Rules
│   ├── use_cases/          # Application-specific business rules
│   ├── ports/              # Interfaces (input/output boundaries)
│   │   ├── input/          # Use case interfaces (driven)
│   │   └── output/         # Repository/service interfaces (driving)
│   └── dto/                # Data Transfer Objects
├── adapters/               # Interface Adapters
│   ├── controllers/        # Input adapters (HTTP, CLI, gRPC)
│   ├── presenters/         # Output formatters
│   ├── repositories/       # Data persistence implementations
│   └── gateways/           # External service implementations
└── infrastructure/         # Frameworks & Drivers
    ├── config/             # Configuration loading
    ├── database/           # DB connections, migrations
    ├── http/               # HTTP server setup
    └── di/                 # Dependency injection container

Critical Patterns

1. Dependency Inversion

WRONG: UseCase depends on concrete Repository
RIGHT: UseCase depends on Repository interface (port)
       Concrete Repository implements the interface

2. Entity Design

// Domain Entity - contains business logic
type Order struct {
    ID        OrderID
    Items     []OrderItem
    Status    OrderStatus
    CreatedAt time.Time
}

func (o *Order) AddItem(item OrderItem) error {
    if o.Status != StatusDraft {
        return ErrOrderNotModifiable
    }
    o.Items = append(o.Items, item)
    return nil
}

func (o *Order) Total() Money {
    var total Money
    for _, item := range o.Items {
        total = total.Add(item.Subtotal())
    }
    return total
}

3. Use Case / Interactor

// Port (interface) - defined in application layer
type OrderRepository interface {
    Save(ctx context.Context, order *Order) error
    FindByID(ctx context.Context, id OrderID) (*Order, error)
}

// Use Case - orchestrates domain logic
type CreateOrderUseCase struct {
    orderRepo OrderRepository
    eventBus  EventPublisher
}

func (uc *CreateOrderUseCase) Execute(ctx context.Context, input CreateOrderInput) (*CreateOrderOutput, error) {
    order := NewOrder(input.CustomerID)
    
    for _, item := range input.Items {
        if err := order.AddItem(item); err != nil {
            return nil, err
        }
    }
    
    if err := uc.orderRepo.Save(ctx, order); err != nil {
        return nil, err
    }
    
    uc.eventBus.Publish(OrderCreatedEvent{OrderID: order.ID})
    
    return &CreateOrderOutput{OrderID: order.ID}, nil
}

4. Repository Implementation (Adapter)

// Adapter - implements the port
type PostgresOrderRepository struct {
    db *sql.DB
}

func (r *PostgresOrderRepository) Save(ctx context.Context, order *Order) error {
    // Convert domain entity to DB model
    model := toOrderModel(order)
    // Persist using infrastructure
    return r.db.Save(ctx, model)
}

func (r *PostgresOrderRepository) FindByID(ctx context.Context, id OrderID) (*Order, error) {
    model, err := r.db.FindByID(ctx, id)
    if err != nil {
        return nil, err
    }
    // Convert DB model back to domain entity
    return toOrderEntity(model), nil
}

Layer Communication Rules

From To Allowed? How
Infrastructure Adapters Yes Direct import
Adapters Application Yes Via ports (interfaces)
Application Domain Yes Direct import
Domain Application NO Never
Application Adapters NO Use dependency injection
Adapters Infrastructure Yes Direct import

Decision Tree: Where Does This Code Go?

Is it a business rule that exists regardless of application?
├─ YES → domain/entities/
└─ NO
   Is it application-specific business logic?
   ├─ YES → application/use_cases/
   └─ NO
      Does it convert data between formats?
      ├─ YES → adapters/
      └─ NO → infrastructure/

Anti-Patterns to Avoid

Anti-Pattern Problem Solution
Anemic Domain Entities with only getters/setters Add business methods to entities
Leaky Abstraction Domain knows about DB/HTTP Use ports for external concerns
Use Case Bloat Too much logic in use cases Extract to domain entities
Shared DTOs Same DTO across layers Create layer-specific DTOs
Direct Infrastructure Controller calls DB directly Always go through use cases

Testing Strategy

Layer Test Type Dependencies
Domain Unit tests None (pure logic)
Application Unit tests Mock ports
Adapters Integration tests Real/test infrastructure
Infrastructure Integration tests Real external systems

Commands

# Typical directory creation for new clean architecture project
mkdir -p domain/{entities,value_objects}
mkdir -p application/{use_cases,ports/{input,output},dto}
mkdir -p adapters/{controllers,repositories,gateways,presenters}
mkdir -p infrastructure/{config,database,http,di}

Resources

  • Reference: Uncle Bob's Clean Architecture book
  • Pattern: Hexagonal Architecture (Ports and Adapters) - related pattern
  • Pattern: Onion Architecture - related pattern
Related skills
Installs
1
First Seen
Apr 9, 2026