Skip to content

bxcodec/golang-ddd-modular-monolith-with-hexagonal

Repository files navigation

Go DDD Modular Monolith with Hexagonal Architecture

Go Version License

A Golang example project demonstrating Domain-Driven Design (DDD) in a Modular Monolith architecture with Hexagonal (Ports and Adapters) pattern.

Read the original Blogpost here:

Please read the first article, then proceed to the second and this repository to understand the reasoning behind the current structure.

Quick Links:

Table of Contents

Architecture Overview

This project combines three powerful architectural patterns:

Modular Monolith

  • Multiple independent modules (payment, payment-settings) in a single deployable unit
  • Each module has clear boundaries and communicates via well-defined interfaces
  • Modules can be developed, tested, and evolved independently
  • All modules share the same process and database, simplifying deployment and transactions

Hexagonal Architecture (Ports & Adapters)

  • Domain logic (hexagon core) is isolated from external concerns
  • Inbound adapters: REST controllers, cron jobs (drive the application)
  • Outbound adapters: Repositories, external APIs (driven by the application)
  • Ports: Interfaces that define contracts between core and adapters

Domain-Driven Design (DDD)

  • Clear separation between domain logic and infrastructure
  • Each module represents a bounded context
  • Domain entities and business rules are at the center

Project Structure

.
├── application/            # Application entry point
├── cmd/                    # CLI commands (Cobra)
│   ├── rest.go            # REST API server command
│   ├── cron_update_payment.go  # Cron job command
│   └── root.go            # Root command configuration
├── modules/               # Business modules (bounded contexts)
│   ├── payment/
│   │   ├── factory/       # Module factory for dependency injection
│   │   ├── internal/
│   │   │   ├── adapter/   # Inbound/Outbound adapters
│   │   │   │   ├── controller/  # REST controllers (inbound)
│   │   │   │   ├── cron/        # Cron jobs (inbound)
│   │   │   │   └── repository/  # Database repository (outbound)
│   │   │   ├── ports/     # Interface definitions
│   │   │   └── service/   # Business logic
│   │   ├── module.go      # Module registration
│   │   └── payment.go     # Domain entities / Public API for the domain/module
│   └── payment-settings/  # Similar structure
├── pkg/                   # Shared utilities
│   ├── config/            # Configuration management
│   ├── dbutils/           # Database utilities
│   ├── errors/            # Error handling
│   ├── logger/            # Logging utilities
│   ├── middlewares/       # HTTP middlewares
│   └── uniqueid/          # ID generation (ULID)
├── migrations/            # Database migrations
└── docker-compose.yml     # Docker setup

Features

  • Clean Architecture with clear separation of concerns
  • Hexagonal Architecture (Ports and Adapters)
  • Domain-Driven Design principles
  • RESTful API using Echo framework
  • PostgreSQL database with migrations
  • Structured logging with zerolog
  • Comprehensive testing (unit and E2E)
  • Docker support
  • Hot reload development with Air
  • CLI commands with Cobra
  • Mock generation with Mockery
  • Code linting with golangci-lint

Prerequisites

  • Go 1.24.4 or higher
  • Docker and Docker Compose (for database)
  • Make (for running commands)

Quick Start

Option A: One-Command Setup (Recommended for First Time)

If you want everything set up automatically:

# Clone the repository
git clone https://github.com/bxcodec/golang-ddd-modular-monolith-with-hexagonal.git
cd golang-ddd-modular-monolith-with-hexagonal

# Copy environment file
cp .env.example .env

# Install dependencies and start everything
make init
make up

The make up command will:

  1. Start PostgreSQL in Docker
  2. Start the application with hot reload (you'll need to run migrations separately, see below)

After the app starts, open a new terminal and run migrations:

# In a new terminal window
cd golang-ddd-modular-monolith-with-hexagonal
make migrate-up
# Press Enter when prompted to apply all migrations

The API will be available at http://localhost:9090

curl http://localhost:9090/health

Expected response:

{
  "status": "ok",
  "mode": "rest",
  "environment": "development"
}

CLI Commands

The application supports multiple commands via Cobra:

Start REST API Server

go run application/main.go rest

Run Cron Job

go run application/main.go cron-update-payment

Options:

  • --batch-size - Number of payments to process in one batch
  • --dry-run - Run in dry-run mode without making changes

Example:

go run application/main.go cron-update-payment --batch-size 100 --dry-run

Development

Hot Reload with Air

Air is automatically started when you run make up. It watches for file changes and rebuilds the application.

Running Tests

# Run unit tests
make test-unit

# Run E2E tests
make test-e2e

# Run all tests
make test-all

# Run tests with detailed output
make tests-complete

Code Formatting

make fmt

This will format your code using:

  • gofmt
  • gofumpt
  • goimports

Linting

make lint

Generate Mocks

make go-generate

This uses Mockery to generate mocks based on the .mockery.yml configuration.

Database Migrations

Apply Migrations

make migrate-up

Rollback Migrations

make migrate-down

Create New Migration

make migrate-create

Drop Database

make migrate-drop

Docker

Build Docker Image

make image-build

Run with Docker Compose

docker compose up

This will start both PostgreSQL and the application.

Makefile Commands

Run make help to see all available commands:

make help

Key commands:

  • make up - Start development environment
  • make down - Stop Docker containers
  • make destroy - Teardown everything (removes volumes)
  • make build - Build the binary
  • make tests - Run tests
  • make lint - Run linter
  • make fmt - Format code
  • make clean - Clean artifacts

Testing

The project includes both unit tests and E2E tests:

Unit Tests

Located alongside the code, testing individual components in isolation using mocks.

E2E Tests

Use testcontainers to spin up real PostgreSQL instances for integration testing.

Test Coverage

make test-all

Coverage reports are generated in coverage-all.out.

Architecture Benefits

Why Modular Monolith?

  • Simpler than microservices while maintaining modularity
  • Easy refactoring to microservices if needed (modules are already isolated)
  • Shared infrastructure reduces operational complexity
  • No network latency between modules

Why Hexagonal Architecture?

  • Business logic is independent of frameworks and tools
  • Easy to swap implementations (e.g., change database, HTTP framework)
  • Highly testable (mock adapters, test ports)
  • Clear separation between what the application does and how it does it

Why DDD?

  • Focus on core domain and domain logic
  • Ubiquitous language between developers and domain experts
  • Clear bounded contexts
  • Better code organization and maintainability

Module Communication

Modules communicate via well-defined ports (interfaces):

// payment module depends on payment-settings module
type IPaymentSettingsPort interface {
    GetPaymentSetting(id string) (PaymentSetting, error)
    // ...
}

// payment module uses the interface, not the concrete implementation
paymentModule := paymentfactory.NewModule(paymentfactory.ModuleConfig{
    DB:                  db,
    PaymentSettingsPort: paymentSettingsModule.Service, // dependency injection
})

This allows:

  • Clear contracts between modules
  • Easy testing with mocks
  • Potential extraction to microservices

Production Deployment

Build Binary

make build

The binary will be created as engine in the project root.

Run Binary

./engine rest

Docker Deployment

docker build -t payment-app:latest .
docker run -p 9090:9090 \
  -e POSTGRES_HOST=your-db-host \
  -e POSTGRES_PASSWORD=your-db-password \
  payment-app:latest rest

Contributing

Contributions are welcome. Please follow these guidelines:

  1. Fork the repository
  2. Create a feature branch
  3. Write tests for new features
  4. Ensure all tests pass
  5. Run linter and formatter
  6. Submit a pull request

License

This project is licensed under the MIT License - see the LICENSE file for details.

Related Resources

Author

Iman Tumorang

Acknowledgments

This project is inspired by various clean architecture implementations and aims to demonstrate a practical approach to building maintainable Go applications.

About

Golang example project following DDD design in Modular Monolith with Hexagonal Architecture

Resources

License

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published