Go Clean Architecture Project Layout
Project Structure
your-project/
├── cmd/
│ ├── api/
│ │ └── main.go # REST API server entry point
│ ├── grpc/
│ │ └── main.go # gRPC server entry point (future)
│ └── migrate/
│ └── main.go # Database migration tool
├── internal/
│ ├── config/
│ │ ├── config.go # Configuration structures
│ │ └── loader.go # Configuration loader
│ ├── domain/
│ │ ├── entity/
│ │ │ ├── user.go # Domain entities
│ │ │ └── product.go
│ │ ├── repository/
│ │ │ ├── user.go # Repository interfaces
│ │ │ └── product.go
│ │ ├── service/
│ │ │ ├── user.go # Service interfaces
│ │ │ └── product.go
│ │ └── external/
│ │ ├── payment.go # External payment service interface
│ │ ├── notification.go # External notification service interface
│ │ ├── auth.go # External auth service interface
│ │ ├── storage.go # External storage service interface
│ │ └── analytics.go # External analytics service interface
│ ├── usecase/
│ │ ├── user/
│ │ │ ├── create_user.go # Use case implementations
│ │ │ ├── get_user.go
│ │ │ └── service.go # Service implementation
│ │ └── product/
│ │ ├── create_product.go
│ │ ├── get_product.go
│ │ └── service.go
│ ├── infrastructure/
│ │ ├── database/
│ │ │ ├── connection.go # Database connection
│ │ │ ├── migrations/
│ │ │ │ ├── 001_create_users.sql
│ │ │ │ └── 002_create_products.sql
│ │ │ └── gorm/
│ │ │ ├── user.go # GORM models
│ │ │ └── product.go
│ │ ├── repository/
│ │ │ ├── user.go # Repository implementations
│ │ │ └── product.go
│ │ ├── cache/
│ │ │ └── redis.go # Cache implementation
│ │ ├── messaging/
│ │ │ └── kafka.go # Message queue
│ │ └── external/
│ │ ├── client/
│ │ │ ├── http_client.go # HTTP client wrapper
│ │ │ └── grpc_client.go # gRPC client wrapper
│ │ ├── payment/
│ │ │ ├── stripe.go # Stripe payment service
│ │ │ └── paypal.go # PayPal payment service
│ │ ├── notification/
│ │ │ ├── email.go # Email service (SendGrid, etc.)
│ │ │ └── sms.go # SMS service (Twilio, etc.)
│ │ ├── auth/
│ │ │ ├── oauth.go # OAuth providers (Google, Facebook)
│ │ │ └── ldap.go # LDAP authentication
│ │ ├── storage/
│ │ │ ├── s3.go # AWS S3 storage
│ │ │ └── gcs.go # Google Cloud Storage
│ │ └── analytics/
│ │ ├── google.go # Google Analytics
│ │ └── mixpanel.go # Mixpanel analytics
│ ├── delivery/
│ │ ├── http/
│ │ │ ├── handler/
│ │ │ │ ├── user.go # HTTP handlers
│ │ │ │ └── product.go
│ │ │ ├── middleware/
│ │ │ │ ├── auth.go # Authentication middleware
│ │ │ │ ├── cors.go # CORS middleware
│ │ │ │ └── logging.go # Logging middleware
│ │ │ ├── router/
│ │ │ │ └── router.go # Route definitions
│ │ │ └── dto/
│ │ │ ├── request/
│ │ │ │ ├── user.go # Request DTOs
│ │ │ │ └── product.go
│ │ │ └── response/
│ │ │ ├── user.go # Response DTOs
│ │ │ └── product.go
│ │ └── grpc/
│ │ ├── handler/
│ │ │ ├── user.go # gRPC handlers
│ │ │ └── product.go
│ │ └── proto/
│ │ ├── user.proto # Protocol buffer definitions
│ │ └── product.proto
│ ├── pkg/
│ │ ├── logger/
│ │ │ └── logger.go # Logging utilities
│ │ ├── validator/
│ │ │ └── validator.go # Request validation
│ │ ├── jwt/
│ │ │ └── jwt.go # JWT utilities
│ │ ├── errors/
│ │ │ └── errors.go # Custom error types
│ │ └── utils/
│ │ └── utils.go # Common utilities
│ └── di/
│ ├── container.go # Dependency injection container
│ ├── wire.go # Wire providers (if using Wire)
│ └── fx.go # Fx modules (if using Fx)
├── api/
│ ├── openapi/
│ │ └── swagger.yaml # OpenAPI/Swagger specs
│ └── proto/
│ └── generated/ # Generated protobuf files
├── deployments/
│ ├── docker/
│ │ ├── Dockerfile
│ │ └── docker-compose.yml
│ └── k8s/
│ ├── deployment.yaml
│ └── service.yaml
├── scripts/
│ ├── build.sh
│ ├── test.sh
│ └── migrate.sh
├── docs/
│ ├── API.md
│ └── ARCHITECTURE.md
├── .env.example
├── .gitignore
├── go.mod
├── go.sum
├── Makefile
└── README.md
Key Architecture Principles
1. Domain Layer (Core Business Logic)
Entities: Core business objects
Repository Interfaces: Data access contracts
Service Interfaces: Business logic contracts
No external dependencies
2. Use Case Layer (Application Logic)
Use Cases: Application-specific business rules
Service Implementations: Orchestrate domain entities
Depends only on domain layer
3. Infrastructure Layer (External Concerns)
Database: GORM implementations
Cache: Redis implementations
Message Queue: Kafka implementations
External APIs: Third-party integrations
4. Delivery Layer (Interface Adapters)
HTTP: REST API handlers, middlewares
gRPC: gRPC handlers and protobuf
DTOs: Request/Response data transfer objects
Recommended Libraries
Core Framework
go
// HTTP Framework
"github.com/gin-gonic/gin"
// ORM
"gorm.io/gorm"
"gorm.io/driver/postgres"
// Dependency Injection (choose one)
"go.uber.org/fx" // Fx - more declarative
"github.com/google/wire" // Wire - code generation
// Configuration
"github.com/spf13/viper"
// Logging
"go.uber.org/zap"
// Validation
"github.com/go-playground/validator/v10"
// JWT
"github.com/golang-jwt/jwt/v5"
Additional Tools
go
// Database Migration
"github.com/golang-migrate/migrate/v4"
// HTTP Client
"github.com/go-resty/resty/v2"
// Circuit Breaker
"github.com/sony/gobreaker"
// Rate Limiting
"golang.org/x/time/rate"
// Testing
"github.com/stretchr/testify"
"github.com/DATA-DOG/go-sqlmock"
// API Documentation
"github.com/swaggo/gin-swagger"
"github.com/swaggo/files"
// gRPC (future)
"google.golang.org/grpc"
"google.golang.org/protobuf"
// Monitoring
"github.com/prometheus/client_golang"
Example Implementation Files
External Service Interface Example
go
// internal/domain/external/payment.go
package external
import (
"context"
"your-project/internal/domain/entity"
)
type PaymentService interface {
ProcessPayment(ctx context.Context, payment *entity.Payment) (*entity.PaymentResult, error)
RefundPayment(ctx context.Context, paymentID string, amount float64) error
GetPaymentStatus(ctx context.Context, paymentID string) (*entity.PaymentStatus, error)
}
type NotificationService interface {
SendEmail(ctx context.Context, to, subject, body string) error
SendSMS(ctx context.Context, to, message string) error
SendPushNotification(ctx context.Context, userID string, message string) error
}
HTTP Client Wrapper Example
go
// internal/infrastructure/external/client/http_client.go
package client
import (
"context"
"time"
"github.com/go-resty/resty/v2"
"github.com/sony/gobreaker"
"golang.org/x/time/rate"
)
type HTTPClient struct {
client *resty.Client
breaker *gobreaker.CircuitBreaker
rateLimiter *rate.Limiter
}
func NewHTTPClient(baseURL string, timeout time.Duration) *HTTPClient {
client := resty.New().
SetBaseURL(baseURL).
SetTimeout(timeout).
SetRetryCount(3).
SetRetryWaitTime(1 * time.Second)
// Circuit breaker settings
breakerSettings := gobreaker.Settings{
Name: "http-client",
MaxRequests: 3,
Interval: 10 * time.Second,
Timeout: 30 * time.Second,
}
return &HTTPClient{
client: client,
breaker: gobreaker.NewCircuitBreaker(breakerSettings),
rateLimiter: rate.NewLimiter(rate.Limit(10), 10), // 10 requests per second
}
}
func (h *HTTPClient) Get(ctx context.Context, url string, result interface{}) error {
if err := h.rateLimiter.Wait
Wait(ctx); err != nil {
return err
}
_, err := h.breaker.Execute(func() (interface{}, error) {
resp, err := h.client.R().
SetContext(ctx).
SetResult(result).
Get(url)
if err != nil {
return nil, err
}
if resp.IsError() {
return nil, fmt.Errorf("HTTP error: %d", resp.StatusCode())
}
return resp, nil
})
return err
}
func (h *HTTPClient) Post(ctx context.Context, url string, body interface{}, result interface{}) error {
if err := h.rateLimiter.Wait
Wait(ctx); err != nil {
return err
}
_, err := h.breaker.Execute(func() (interface{}, error) {
resp, err := h.client.R().
SetContext(ctx).
SetBody(body).
SetResult(result).
Post(url)
if err != nil {
return nil, err
}
if resp.IsError() {
return nil, fmt.Errorf("HTTP error: %d", resp.StatusCode())
}
return resp, nil
})
return err
}
External Payment Service Implementation
go
// internal/infrastructure/external/payment/stripe.go
package payment
import (
"context"
"your-project/internal/domain/entity"
"your-project/internal/domain/external"
"your-project/internal/infrastructure/external/client"
)
type stripeService struct {
client *client.HTTPClient
apiKey string
}
func NewStripeService(apiKey string) external.PaymentService {
httpClient := client.NewHTTPClient("https://api.stripe.com", 30*time.Second)
return &stripeService{
client: httpClient,
apiKey: apiKey,
}
}
func (s *stripeService) ProcessPayment(ctx context.Context, payment *entity.Payment) (*entity.PaymentResult, er
// Stripe API call implementation
var result entity.PaymentResult
requestBody := map[string]interface{}{
"amount": payment.Amount,
"currency": payment.Currency,
"source": payment.Token
Token,
}
err := s.client.Post(ctx, "/v1/charges", requestBody, &result)
if err != nil {
return nil, err
}
return &result, nil
}
func (s *stripeService) RefundPayment(ctx context.Context, paymentID string, amount float64) error {
requestBody := map[string]interface{}{
"charge": paymentID,
"amount": amount,
}
return s.client.Post(ctx, "/v1/refunds", requestBody, nil)
}
func (s *stripeService) GetPaymentStatus(ctx context.Context, paymentID string) (*entity.PaymentStatus, error) {
var status entity.PaymentStatus
err := s.client.Get(ctx, "/v1/charges/"+paymentID, &status)
return &status, err
}
External Notification Service Implementation
go
// internal/infrastructure/external/notification/email.go
package notification
import (
"context"
"your-project/internal/domain/external"
"your-project/internal/infrastructure/external/client"
)
type emailService struct {
client *client.HTTPClient
apiKey string
}
func NewEmailService(apiKey string) external.NotificationService {
httpClient := client.NewHTTPClient("https://api.sendgrid.com", 30*time.Second)
return &emailService{
client: httpClient,
apiKey: apiKey,
}
}
func (e *emailService) SendEmail(ctx context.Context, to, subject, body string) error {
requestBody := map[string]interface{}{
"personalizations": []map[string]interface{}{
{
"to": []map[string]string{
{"email": to},
},
},
},
"from": map[string]string{
"email": "noreply@yourapp.com",
},
"subject": subject,
"content": []map[string]string{
{
"type": "text/html",
"value": body,
},
},
}
return e.client.Post(ctx, "/v3/mail/send", requestBody, nil)
}
func (e *emailService) SendSMS(ctx context.Context, to, message string) error {
// Implementation for SMS via Twilio or similar
return nil
}
func (e *emailService) SendPushNotification(ctx context.Context, userID string, message string) error {
// Implementation for push notifications
return nil
}
Use Case with External Service
go
// internal/usecase/user/service.go
package user
import (
"context"
"your-project/internal/domain/entity"
"your-project/internal/domain/external"
"your-project/internal/domain/repository"
"your-project/internal/domain/service"
)
type userService struct {
userRepo repository.UserRepository
paymentService external.PaymentService
notificationSvc external.NotificationService
}
func NewUserService(
userRepo repository.UserRepository,
paymentService external.PaymentService,
notificationSvc external.NotificationService,
) service.UserService {
return &userService{
userRepo: userRepo,
paymentService: paymentService,
notificationSvc: notificationSvc,
}
}
func (s *userService) CreatePremiumUser(ctx context.Context, user *entity.User, payment *entity.Payment) error {
// Process payment first
result, err := s.paymentService.ProcessPayment(ctx, payment)
if err != nil {
return err
}
// Create user if payment successful
user.IsPremium = true
user.PaymentID = result.ID
if err := s.userRepo.Create(ctx, user); err != nil {
// Rollback payment if user creation fails
s.paymentService.RefundPayment(ctx, result.ID, payment.Amount)
return err
}
// Send welcome email
go s.notificationSvc.SendEmail(ctx, user.Email, "Welcome to Premium!", "Thank you for upgrading!")
return nil
}
Repository Interface Example
go
// internal/domain/repository/user.go
package repository
import (
"context"
"your-project/internal/domain/entity"
)
type UserRepository interface {
Create(ctx context.Context, user *entity.User) error
GetByID(ctx context.Context, id uint64) (*entity.User, error)
GetByEmail(ctx context.Context, email string) (*entity.User, error)
Update(ctx context.Context, user *entity.User) error
Delete(ctx context.Context, id uint64) error
}
Use Case Service Example
go
// internal/usecase/user/service.go
package user
import (
"context"
"your-project/internal/domain/entity"
"your-project/internal/domain/repository"
"your-project/internal/domain/service"
)
type userService struct {
userRepo repository.UserRepository
}
func NewUserService(userRepo repository.UserRepository) service.UserService {
return &userService{
userRepo: userRepo,
}
}
func (s *userService) CreateUser(ctx context.Context, user *entity.User) error {
if err := user.Validate
Validate(); err != nil {
return err
}
return s.userRepo.Create(ctx, user)
}
HTTP Handler Example
go
// internal/delivery/http/handler/user.go
package handler
import (
"net/http"
"your-project/internal/delivery/http/dto/request"
"your-project/internal/delivery/http/dto/response"
"your-project/internal/domain/service"
"github.com/gin-gonic/gin"
)
type UserHandler struct {
userService service.UserService
}
func NewUserHandler(userService service.UserService) *UserHandler {
return &UserHandler{
userService: userService,
}
}
func (h *UserHandler) CreateUser(c *gin.Context) {
var req request.CreateUserRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// Convert DTO to entity and call service
// Return response
}
Dependency Injection with Fx
Fx Module Example
go
// internal/di/fx.go
package di
import (
"your-project/internal/delivery/http/handler"
"your-project/internal/infrastructure/repository"
"your-project/internal/usecase/user"
"go.uber.org/fx"
)
var Module = fx.Options(
// Infrastructure
fx.Provide(repository.NewUserRepository),
// Use Cases
fx.Provide(user.NewUserService),
// Handlers
fx.Provide(handler.NewUserHandler),
// Server
fx.Provide(NewGinServer),
)
Main Application
go
// cmd/api/main.go
package main
import (
"your-project/internal/di"
"go.uber.org/fx"
)
func main() {
fx.New(
di.Module,
fx.Invoke(func(*gin.Engine) {}), // Start server
).Run()
}
Benefits of This Structure
1. Clean Separation: Each layer has clear responsibilities
2. Testability: Easy to mock interfaces for testing
3. Maintainability: Changes in one layer don't affect others
4. Scalability: Easy to add new features and services
5. Flexibility: Can switch implementations without changing business logic
6. Future-proof: Ready for gRPC and other delivery mechanisms
Getting Started
1. Initialize your Go module
2. Set up the directory structure
3. Implement domain entities and interfaces
4. Create use case implementations
5. Build infrastructure layer (database, cache)
6. Implement delivery layer (HTTP handlers)
7. Wire everything together with dependency injection
8. Add tests for each layer
This structure provides a solid foundation for a maintainable, scalable Go application following clean
architecture principles.