Skip to content
go-clean-architecture logo

Go Clean Architecture Skill

go-clean-architecture

Expert knowledge in Go clean architecture patterns and best practices

SKILL.md

Full skill instructions

Go Clean Architecture Skill

Overview

Clean Architecture in Go emphasizes separation of concerns through distinct layers, with dependencies pointing inward toward the domain.

Layer Structure

Domain Layer (innermost)

Location: internal/​domain/

Contains:

  • Business entities (structs)
  • Repository interfaces
  • Domain logic and validation
  • Business rules

Rules:

  • NO external dependencies
  • NO framework dependencies
  • Pure business logic
  • Defines contracts for outer layers

Example:

// internal/​domain/​account.go
package domain

type Account struct {
    ID      string
    Name    string
    Type    AccountType
    Balance int // cents
}

type AccountRepository interface {
    Create(account *Account) error
    GetByID(id string) (*Account, error)
    Update(account *Account) error
    Delete(id string) error
}

// Domain validation
func (a *Account) Validate() error {
    if a.Name == "" {
        return ErrInvalidName
    }
    if !a.Type.IsValid() {
        return ErrInvalidType
    }
    return nil
}

Application Layer (middle)

Location: internal/​application/

Contains:

  • Business logic services
  • Use case orchestration
  • Service interfaces
  • Cross-cutting concerns

Rules:

  • Depends ONLY on domain interfaces
  • NO HTTP dependencies
  • NO database dependencies
  • Orchestrates domain entities

Example:

// internal/​application/​account_service.go
package application

import "internal/​domain"

type AccountService struct {
    repo domain.AccountRepository // Interface, not concrete type
}

func NewAccountService(repo domain.AccountRepository) *AccountService {
    return &AccountService{repo: repo}
}

func (s *AccountService) CreateAccount(account *domain.Account) error {
    if err := account.Validate(); err != nil {
        return fmt.Errorf("validation failed: %w", err)
    }

    if err := s.repo.Create(account); err != nil {
        return fmt.Errorf("failed to create account: %w", err)
    }

    return nil
}

Infrastructure Layer (outermost)

Location: internal/​infrastructure/

Contains:

  • Repository implementations
  • HTTP handlers
  • Database logic
  • External service integrations

Rules:

  • Implements domain interfaces
  • Can have external dependencies
  • Handlers should be thin (parse → service → respond)
  • Repositories only handle persistence

Example:

// internal/​infrastructure/​repository/​account_repository.go
package repository

import (
    "database/​sql"
    "internal/​domain"
)

type AccountRepository struct {
    db *sql.DB
}

func NewAccountRepository(db *sql.DB) *AccountRepository {
    return &AccountRepository{db: db}
}

func (r *AccountRepository) Create(account *domain.Account) error {
    query := `INSERT INTO accounts (id, name, type, balance) VALUES (?, ?, ?, ?)`
    _, err := r.db.Exec(query, account.ID, account.Name, account.Type, account.Balance)
    return err
}

// internal/​infrastructure/​http/​handlers/​account_handler.go
package handlers

type AccountHandler struct {
    service *application.AccountService
}

func (h *AccountHandler) CreateAccount(w http.ResponseWriter, r *http.Request) {
    // 1. Parse request
    var req CreateAccountRequest
    if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
        http.Error(w, "invalid request", http.StatusBadRequest)
        return
    }

    // 2. Call service
    account := req.ToDomain()
    if err := h.service.CreateAccount(account); err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }

    // 3. Return response
    w.WriteHeader(http.StatusCreated)
    json.NewEncoder(w).Encode(account)
}

Dependency Injection

Wire dependencies in main.go:

// cmd/​server/​main.go
func main() {
    // Infrastructure
    db := setupDatabase()

    // Repositories (concrete implementations)
    accountRepo := repository.NewAccountRepository(db)

    // Services (injected with interfaces)
    accountService := application.NewAccountService(accountRepo)

    // Handlers (injected with services)
    accountHandler := handlers.NewAccountHandler(accountService)

    // Router
    router := setupRouter(accountHandler)

    http.ListenAndServe(":8080", router)
}

Common Patterns

Repository Pattern

// Domain defines interface
type Repository interface {
    Create(entity *Entity) error
    GetByID(id string) (*Entity, error)
}

// Infrastructure implements
type SQLRepository struct {
    db *sql.DB
}

func (r *SQLRepository) Create(entity *Entity) error {
    // SQL implementation
}

Service Pattern

type Service struct {
    repo domain.Repository  // Depend on interface
}

func (s *Service) DoBusinessLogic(entity *domain.Entity) error {
    // Validate
    // Transform
    // Call repository
    return s.repo.Create(entity)
}

Handler Pattern

func (h *Handler) HandleRequest(w http.ResponseWriter, r *http.Request) {
    // Parse → Service → Respond
    req := parseRequest(r)
    result, err := h.service.Do(req)
    respond(w, result, err)
}

Anti-Patterns to Avoid

❌ Domain with External Dependencies

// BAD: Domain importing database
import "database/​sql"

type Account struct {
    db *sql.DB  // ❌ Domain shouldn't know about database
}

❌ Service with HTTP/​Database

// BAD: Service with HTTP dependency
func (s *Service) Create(w http.ResponseWriter, r *http.Request) {
    // ❌ Service shouldn't handle HTTP
}

// BAD: Service with database dependency
func (s *Service) Create(db *sql.DB, entity *Entity) error {
    // ❌ Service should use repository interface
}

❌ Handler with Business Logic

// BAD: Complex logic in handler
func (h *Handler) Create(w http.ResponseWriter, r *http.Request) {
    // Parse
    // ❌ Complex validation
    // ❌ Calculations
    // ❌ Business rules
    // Direct database access
}

// GOOD: Thin handler
func (h *Handler) Create(w http.ResponseWriter, r *http.Request) {
    req := parse(r)
    result := h.service.Create(req)  // Service has the logic
    respond(w, result)
}

❌ Repository with Business Logic

// BAD: Business rules in repository
func (r *Repository) Create(account *Account) error {
    // ❌ Business validation in repository
    if account.Balance < 0 && account.Type != "credit" {
        return errors.New("invalid")
    }
    // Should only handle persistence
}

Testing Strategy

Domain Tests

func TestAccount_Validate(t *testing.T) {
    // Test entity validation
    // No mocks needed
}

Service Tests (Unit)

func TestService_Create(t *testing.T) {
    mockRepo := &MockRepository{}  // Mock interface
    service := NewService(mockRepo)
    // Test business logic
}

Repository Tests (Integration)

func TestRepository_Create(t *testing.T) {
    db := setupTestDB()  // Real database
    repo := NewRepository(db)
    // Test persistence
}

Handler Tests (E2E)

func TestHandler_Create(t *testing.T) {
    mockService := &MockService{}
    handler := NewHandler(mockService)
    req := httptest.NewRequest("POST", "/", body)
    w := httptest.NewRecorder()
    handler.Create(w, req)
    // Test HTTP layer
}

Benefits

✅ Testability: Easy to mock dependencies ✅ Maintainability: Clear separation of concerns ✅ Flexibility: Easy to swap implementations ✅ Independence: Domain logic independent of frameworks ✅ Scalability: Easy to add features

When to Apply

  • Multi-layer applications
  • Complex business logic
  • Long-lived projects
  • Team projects requiring clear boundaries
  • Applications that may change databases/​frameworks

Quick Checklist

  • Domain has no external dependencies
  • Application uses interfaces, not concrete types
  • Handlers are thin (parse → service → respond)
  • Repositories only handle persistence
  • Dependencies point inward
  • Business logic in services, not handlers
  • Each layer has clear responsibility

More skills from majiayu000

xiaohongshu logo
majiayu000/claude-arsenal

xiaohongshu

xiaohongshu

286 148
View
agent-task-conductor logo
majiayu000/claude-skill-registry

agent-task-conductor

Conduct multi-agent task orchestration and workflow coordination.

663 1
View
conductor-setup logo
majiayu000/claude-skill-registry

conductor-setup

Initialize project with Conductor artifacts (product definition,

663 1
View
animation-designer logo
majiayu000/claude-skill-registry

animation-designer

Expert in web animations, transitions, and motion design using Framer Motion and CSS

663 1
View
diagramming logo
majiayu000/claude-skill-registry

diagramming

Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...

663 1
View
h3-pg logo
majiayu000/claude-skill-registry-data

h3-pg

PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.

23 1
View
conductor-development logo
majiayu000/claude-skill-registry

conductor-development

Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...

663 1
View
conductor-status logo
majiayu000/claude-skill-registry

conductor-status

Display project status, active tracks, and next actions

663 1
View
dockerization logo
majiayu000/claude-skill-registry

dockerization

Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.

663 1
View

Popular AI tools

Kaiber logo
Video

Kaiber

Generate, edit, and beat-sync AI video with leading models in one workspace.

Paid
View
Vimcal logo
Productivity

Vimcal

The world's fastest calendar for remote work

Free
View

Transform Your Design with AI Designer by ImgCreator.ai

Freemium
View
Akool AI logo
Content & writing

Akool AI

Revolutionizing Video Production with AI-Powered Creativity

Paid
View

Extend an image past the frame and let AI fill the new aspect ratio.

Freemium
View
StarByFace logo
Security

StarByFace

Discover your celebrity doppelgänger with StarByFace!

Free
View
C

ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.

Freemium
View
Opus Clip logo
Coding & apps

Opus Clip

Opus.ai: Revolutionize Your Web Experience

Free
View