KEMBAR78
Go Clean Architecture Project Layout | PDF | Databases | Software Engineering
0% found this document useful (0 votes)
15 views21 pages

Go Clean Architecture Project Layout

The document outlines a Go Clean Architecture project structure, detailing the organization of directories and files for various components such as command entry points, internal logic, infrastructure, and delivery layers. It emphasizes key architecture principles, including the separation of domain, use case, infrastructure, and delivery layers, along with recommended libraries and tools for development. Additionally, it provides examples of implementation files for external services, HTTP clients, and use case services.

Uploaded by

Dao Vy
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
15 views21 pages

Go Clean Architecture Project Layout

The document outlines a Go Clean Architecture project structure, detailing the organization of directories and files for various components such as command entry points, internal logic, infrastructure, and delivery layers. It emphasizes key architecture principles, including the separation of domain, use case, infrastructure, and delivery layers, along with recommended libraries and tools for development. Additionally, it provides examples of implementation files for external services, HTTP clients, and use case services.

Uploaded by

Dao Vy
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 21

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.

You might also like