Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save pedrovasconcellos/068354bbbde9a4d6957150056ab2a138 to your computer and use it in GitHub Desktop.
Save pedrovasconcellos/068354bbbde9a4d6957150056ab2a138 to your computer and use it in GitHub Desktop.
Clean Architecture folder structure

Clean Architecture

Folder structure

.                                     # Repository root
├── .github/                          # CI/CD workflows configurations
│   └── workflows/
│       ├── ci.yml                    # Test and lint pipeline
│       └── cd.yml                    # Deploy pipeline
├── docs/                             # Project documentation
│   ├── architecture.md               # General architecture description
│   ├── api.md                        # REST API documentation
│   └── deployment.md                 # Deployment and infrastructure guide
├── src/                              # Main module source code
│   ├── cmd/                          # Entry points (executables)
│   │   ├── api/                      # HTTP server
│   │   │   └── main.go               # API server initialization
│   │   ├── worker/                   # Worker for SQS queue processing
│   │   │   └── main.go               # Worker initialization
│   │   └── cli/                      # Command line tool
│   │       └── main.go               # CLI initialization
│   ├── configs/                      # Configuration files (YAML, JSON, TOML)
│   │   ├── config.yaml               # Generic configuration
│   │   ├── config.dev.yaml           # Development environment configuration
│   │   └── config.prod.yaml          # Production environment configuration
│   ├── internal/                     # Internal code (not exportable)
│   │   ├── domain/                   # Pure domain layer
│   │   │   ├── entities/             # Business entities and models
│   │   │   │   └── order.go          # Order struct
│   │   │   │── repositories/         # Abstract repository interfaces
│   │   │   │   └── i_order_repository.go # IOrderRepository contract
│   │   │   ├── services/             # Domain Services (business rules)
│   │   │   │   ├── interfaces/       # Domain Service contracts
│   │   │   │   │   └── i_order_service_domain.go # IOrderServiceDomain contract
│   │   │   │   └── implementations/  # Concrete Domain Service implementations
│   │   │   │       └── order_service_domain.go # Concrete Domain Service
│   │   │   └── valueobjects/         # Value Objects (e.g., Money, Address)
│   │   │       └── money.go          # VO for monetary values
│   │   ├── application/              # Use cases and logic orchestration
│   │   │   ├── dto/                  # Data Transfer Objects grouped by type
│   │   │   │   ├── requests/         # Request DTOs
│   │   │   │   │   ├── create_order_request.go # CreateOrder request
│   │   │   │   │   └── list_orders_request.go  # ListOrders request
│   │   │   │   └── responses/        # Response DTOs
│   │   │   │       ├── create_order_response.go # CreateOrder response
│   │   │   │       └── list_orders_response.go  # ListOrders response
│   │   │   ├── interfaces/           # Use Case contracts
│   │   │   │   ├── i_create_order_usecase.go   # ICreateOrderUseCase contract
│   │   │   │   └── i_list_orders_usecase.go    # IListOrdersUseCase contract
│   │   │   ├── usecases/             # Use Case implementations
│   │   │   │   ├── create_order_usecase.go   # CreateOrderUseCase implementation
│   │   │   │   └── list_orders_usecase.go    # ListOrdersUseCase implementation
│   │   ├── adapters/                 # Input/output adapters
│   │   │   ├── http/                 # HTTP/REST adapters
│   │   │   │   ├── handlers/         # Handlers invoking Use Cases
│   │   │   │   │   └── order_handler.go # Concrete REST handler
│   │   │   │   └── router.go         # HTTP routing
│   │   │   ├── sqs/                  # AWS SQS queue adapters
│   │   │   │   └── order_events.go   # SQS event processing
│   │   │   ├── aws_lambda/           # AWS Lambda adapters
│   │   │   │   └── lambda_handler.go # Lambda handler
│   │   │   └── grpc/                 # gRPC adapters (optional)
│   │   │       ├── proto/            # .proto files for gRPC
│   │   │       └── server.go         # gRPC server
│   │   ├── infrastructure/           # Concrete infrastructure implementations
│   │   │   ├── config/               # Configuration loader
│   │   │   │   └── loader.go         # Loads config.yaml files
│   │   │   ├── mongodb/              # MongoDB client and repository
│   │   │   │   ├── client.go         # Initializes MongoDB connection
│   │   │   │   └── order_repository.go # Concrete MongoDB repository
│   │   │   ├── aws/                  # Generic AWS SDK
│   │   │   │   ├── sqs/              # SQS client/wrapper
│   │   │   │   │   └── client.go     # Message enqueueing and consumption
│   │   │   │   └── s3/               # S3 client/wrapper
│   │   │   │       └── client.go     # Object upload/download
│   │   │   ├── cache/                # Cache implementation (Redis)
│   │   │   │   └── client.go         # Redis client
│   │   │   ├── logger/               # Logger configuration (Zap, Logrus…)
│   │   │   │   └── zap.go            # Logger initialization
│   │   │   └── metrics/              # Prometheus instrumentation and metrics
│   │   │       └── prometheus.go     # Exposes metrics
│   │── shared/                       # Reusable exportable packages
│   │   ├── errors/                   # Custom error handling
│   │   │   └── error.go              # Common error definitions
│   │   └── utilities/                # Generic helper functions
│   │       └── datetime_util.go      # General helpers
├── scripts/                          # Auxiliary scripts (migrations, seeds, deploy)
│   ├── migrate.sh                    # Database migrations via script
│   ├── seed.sh                       # Populate initial data
│   └── docker-entrypoint.sh          # Custom Docker entrypoint
├── migrations/                       # Versioned MongoDB migrations
│   └── 20250529_create_orders_collection.js
├── test/                             # Automated tests
│   ├── units/                        # Unit tests
│   ├── integrations/                 # Integration tests (MongoDB, SQS)
├── Makefile                          # Build, test, lint, run shortcuts
├── Dockerfile                        # Docker image definition for the application
├── docker-compose.yml                # Service orchestration for local dev
├── README.md                         # Quick start guide and overview
└── go.mod / go.sum                   # Go module definitions and dependencies

Brief description of the layers:

  • .github/workflows/: CI/CD pipelines for lint, tests, and deploy.
  • docs/: Architecture, API, and deployment documentation.
  • cmd/: Application entry points (e.g., HTTP server, workers, CLI).
  • configs/: Configuration files (YAML, JSON).
  • internal/domain/: Entities and abstract interfaces without external dependencies.
  • internal/application/: Use cases and business logic orchestration.
  • internal/adapters/: Input/output adapters (HTTP, SQS, Lambda).
  • internal/infrastructure/: Concrete implementations (MongoDB, AWS SQS, AWS S3, logger).
  • shared/: General-purpose libraries and utilities.
  • scripts/: Auxiliary scripts for database operations and deployment.
  • migrations/: Versioned scripts to create/alter MongoDB collections.
  • test/: Separates integration and unit tests.
  • Makefile: Development workflow conveniences.
  • Dockerfile/docker-compose.yml: Containerization and service orchestration.
  • go.mod/go.sum: Project dependencies and versioning.

Disclaimer: I know that in Go it's not idiomatic to prefix interface names with 'I', but I prefer to use this pattern because it has always provided me with faster file identification when using my development IDE.

Why do we have the adapters folder?

  • Protocol isolation: Translates requests (HTTP, SQS, etc.) into the formats expected by use cases without contaminating application with transport details.
  • Dependency rule: Outer layers (interfaces) depend on inner layers (application and domain), but not vice versa.
  • Flexibility: Allows exposing the same use cases via multiple channels (REST, CLI, Lambda) by creating new adapters without touching business logic.
  • Separation of responsibilities: Each layer focuses on its responsibility: pure domain, use case orchestration, and I/O adaptation.
  • Testability: Well-isolated adapters make it easier to write unit tests for use cases and domain services by mocking inputs/outputs without raising real HTTP servers, queues, or connections.

What does the cmd/ folder mean?

The cmd/ convention groups the application entry points. Each subfolder represents a distinct executable:

cmd/
├── api/
│   └── main.go
├── worker/
│   └── main.go
├── cli/
│   └── main.go

In the main.go of cmd/api, for example (example for aws lambda):

package main

import (
    "net/http"
    "myproject/internal/interfaces/http"
    "myproject/internal/infrastructure/config"
    "myproject/internal/infrastructure/logger"
)

func main() {
    cfg := config.Load()
    log := logger.New(cfg.LogLevel)
    router := http.NewRouter(log, cfg)
    
    log.Info("Starting API on port: ", cfg.Port)
    http.ListenAndServe(":"+cfg.Port, router)
}
  • Responsibility: Assemble the application, load configurations, initialize clients (MongoDB, SQS, S3), instantiate handlers, and start the server or worker.
  • Inverted dependency: cmd/ imports internal layers, but they must not import cmd/, keeping business logic independent of how it’s executed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment