How to Implement Clean Architecture in Go: Complete Example
Clean Architecture in Go is implemented by separating your code into distinct layers: Domain, Application, Infrastructure, and Interface, where dependencies point inward toward the core business logic. Start by defining your domain entities and interfaces in a domain package, then implement use cases in an application package that depend only on those interfaces, and finally wire concrete implementations (like databases or HTTP handlers) in infrastructure and interface packages.
// 1. Domain: Define core entities and interfaces
// File: domain/user.go
package domain
type User struct {
ID string
Name string
}
type UserRepository interface {
GetByID(id string) (*User, error)
}
// 2. Application: Define use cases depending on domain interfaces
// File: application/user_service.go
package application
import "yourmodule/domain"
type UserService struct {
repo domain.UserRepository
}
func (s *UserService) GetUser(id string) (*domain.User, error) {
return s.repo.GetByID(id)
}
// 3. Infrastructure: Implement domain interfaces (e.g., SQL)
// File: infrastructure/postgres_repo.go
package infrastructure
import (
"database/sql"
"yourmodule/domain"
)
type PostgresUserRepo struct {
db *sql.DB
}
func (r *PostgresUserRepo) GetByID(id string) (*domain.User, error) {
// SQL logic here
var u domain.User
// err := r.db.QueryRow("SELECT id, name FROM users WHERE id = ?", id).Scan(&u.ID, &u.Name)
// if err != nil { return nil, err }
return &u, nil
}
// 4. Interface: Wire everything together (e.g., HTTP Handler)
// File: interface/http_handler.go
package httpinterface
import (
"encoding/json"
"net/http"
"yourmodule/application"
"yourmodule/domain"
)
func NewHTTPHandler(repo domain.UserRepository) http.Handler {
service := &application.UserService{repo: repo}
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
user, err := service.GetUser(r.URL.Query().Get("id"))
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
json.NewEncoder(w).Encode(user)
})
}
To run your application, initialize the concrete infrastructure components in main.go and pass them to the interface layer to create the final handler.