π§ Applying Clean Architecture in Go β A Real-World Project Walkthrough
Clean Architecture is a popular approach for building scalable and maintainable backend systems. However, applying it effectively β especially in real-world projects β can feel overwhelming without concrete examples.
In this post, Iβd like to share how Iβve applied Clean Architecture principles in a simple Go project called ποΈ Ticket Reservation System. Itβs a small concert ticket booking service, and while not exhaustive, it demonstrates one possible way to organize code using Clean Architecture. The project includes features like domain-driven modeling, repository interfaces, CLI tooling, Swagger documentation, and modular routing.
If youβre looking for a starting point to apply Clean Architecture in your own Go projects, this layout might offer a useful reference.
π§ Why Clean Architecture?
Clean architecture promotes separation of concerns, testability, and clear boundaries between core business logic and external dependencies like databases, HTTP frameworks, or third-party APIs.
This means:
- Business logic doesnβt depend on frameworks.
- Infrastructure can change without rewriting your use cases.
- Handlers, services, and domain models remain isolated and testable.
π§± Folder Structure Breakdown
Hereβs the high-level layout of the ticket-reservation project:
ticket-reservation/
βββ cmd/ # Application entry points and CLI commands
β βββ root.go # Root command for the CLI
β βββ serve_cmd.go # Command to start the server
β βββ migrate_cmd.go # Command to run database migrations
β βββ new_migration_cmd.go # Command to create new migration files
β βββ print_config_cmd.go # Command to print the current configuration
β βββ generate_sql_builder.go # Command to generate SQL builder files
β βββ ... # Other commands
βββ db/ # Database-related files
β βββ migrations/ # Database migration files
βββ docs/ # Documentation files
β βββ swagger.yaml # API documentation in Swagger format
β βββ ... # Other design and architecture documents
βββ internal/ # Internal application code
β βββ api/ # Delivery layer
β β βββ http/ # HTTP API components
β β βββ handler/ # HTTP handlers by domain
β β β βββ healthcheck/ # Health check handlers
β β β βββ ... # Other domain handlers
β β βββ middleware/ # HTTP middleware
β β βββ route/ # HTTP route definitions
β βββ config/ # Application configuration
β βββ domain/ # Domain layer
β β βββ cache/ # Cache-related interfaces
β β βββ entity/ # Domain models (e.g., Concert, Reservation)
β β βββ errs/ # Domain-specific errors
β β βββ repository/ # Repository interfaces
β βββ infra/ # Infrastructure layer
β β βββ db/ # Database implementations
β β β βββ connection.go # Database connection setup
β β β βββ sql_execer.go # SQL execution interface
β β β βββ transactor.go # Transaction management
β β β βββ mocks/ # Mock implementations
β β β βββ model_gen/ # Generated models from DB schema
β β β βββ repository/ # Repository implementations
β β β βββ healthcheck/ # Health check repository
β β β βββ ... # Other repositories
β β βββ redis/ # Redis implementations
β β βββ client.go # Redis client interface
β β βββ connection.go # Redis connection setup
β β βββ mocks/ # Mock implementations
β β βββ repository/ # Repository implementations
β β βββ seat/ # Seat locking and cache implementations
β β βββ ... # Other repositories
β βββ server/ # Server setup and initialization
β β βββ dependency.go # Dependency injection
β β βββ middleware.go # Server middleware
β β βββ server.go # HTTP server setup
β βββ usecase/ # Application business logic
β β βββ healthcheck/ # Health check use case
β β βββ ... # Other use cases
β βββ util/ # Utility functions
β βββ httpresponse/ # HTTP response helpers
β βββ ... # Other utility functions
βββ pkg/ # Shared helper packages
βββ docker-compose.yaml # Docker Compose for local development
βββ Dockerfile # Docker build definition
βββ env.yaml # Environment variables configuration
βββ go.mod # Go module definition
βββ go.sum # Go module dependencies
βββ main.go # Main application entry point
βββ Makefile # Makefile for common tasks
βββ README.md # Project documentation
Letβs walk through some highlights π
π§© Clean Architecture in Action
1. Delivery Layer β internal/api/http/
This layer exposes the API using the Gin framework. It includes:
-
handler/
: request parsing, validation, response formatting -
middleware/
: application-level HTTP middleware such as authentication -
route/
: HTTP route setup using injected dependencies
2. Usecase Layer β internal/usecase/
This layer contains pure application logic. It defines input/output structs and orchestrates repository calls or business rules. It does not depend on Gin, SQL, or any infra packages.
3. Domain Layer β internal/domain/
This is the heart of the application. It defines:
-
entity/
: domain models like Concert, Reservation -
repository/
: abstract interfaces for data access -
mocks/
: auto-generated mocks for unit tests
No dependencies, no framework imports β just business terms and contracts.
4. Infrastructure Layer β internal/infra/
This layer implements database logic using go-jet and sqlx. It satisfies the interfaces defined in the domain layer.
π οΈ Built-in CLI Tools
This project comes with a built-in CLI (powered by Cobra) for common dev tasks. Run go run main.go --help
to explore all commands.
Key commands:
-
serve
: Start the HTTP server -
migrate
: Run database migrations -
new-migration
: Generate new migration files -
print-config
: Print current effective config -
generate-db
: Generate SQL builder and model files using go-jet
π Project Status
This project is still a work-in-progress, and not all features are complete. However, it already provides a useful reference for:
- Structuring Go services with Clean Architecture
- Organizing code for testability and scalability
- Using tools like Cobra, Swagger, Jet, and MockGen effectively
If youβre starting a backend service in Go and want a clean, extensible foundation β feel free to fork or study this project. Feedback and contributions are welcome!
π GitHub Repo β ticket-reservation
Top comments (0)