A production-ready Todo application built with Go, demonstrating clean hexagonal architecture (ports and adapters pattern) with flexible deployment options and storage backends.
This project implements hexagonal architecture to achieve a clean separation of concerns and maximum flexibility:
┌──────────────────────────────────────────────────┐
│ Driving Adapters │
│ (Primary/Input) │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ REST API │ │ CLI │ │
│ │ (HTTP) │ │ (Cobra) │ │
│ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │
└─────────┼─────────────────────────────┼──────────┘
│ │
└──────────┬──────────────────┘
│
┌──────────▼──────────┐
│ Application Core │
│ (Business Logic) │
│ Service Layer │
└──────────┬──────────┘
│
┌────────────▼────────────┐
│ Repository Interface │
│ (Port) │
└────────────┬────────────┘
│
┌───────────────┴──────────────┐
│ │
┌────▼─────┐ ┌─────▼──────┐
│ MongoDB │ │ In-Memory │
│ Adapter │ │ Store │
└──────────┘ └────────────┘
Driven Adapters (Secondary/Output)
- Driving Adapters (Primary): REST API and CLI interfaces that drive the application
- Service Port: Interface defining available business logic. Driving adapters call functions on this interface to interact with the service layer
- Application Core: Business logic isolated from external concerns
- Repository Port: Interface defining storage operations
- Driven Adapters (Secondary): MongoDB and in-memory implementations of the repository
- Hexagonal Architecture: Clean separation between business logic and infrastructure
- Multiple Interfaces: Run as REST API server or CLI application
- Pluggable Storage: Switch between MongoDB and in-memory storage via configuration
- Configuration Management: Environment-based config with Viper, supporting both env vars and config files
- Validation: Comprehensive config validation with cross-field dependencies
- Structured Logging: Production-ready logging with Zap
- Graceful Shutdown: Proper cleanup of resources on application termination
- Vendored Dependencies: All dependencies vendored for reliable builds with private packages
- Go 1.24 or higher
- MongoDB (optional, only if using database storage)
- Make (optional, for using Makefile commands)
git clone https://github.com/mwinyimoha/go-todos.git
cd go-todosmake build
# Or directly:
go build -mod=vendor -o build/go-todos ./cmdThe application uses Viper for configuration management. Configuration can be provided via:
- Environment variables
.envfile in the project root- Default values (for development)
| Variable | Description | Default | Required | Validation |
|---|---|---|---|---|
APP_NAME |
Application name | Todos Service | Yes | - |
APP_VERSION |
Application version | 0.1.0 |
Yes | - |
APP_TIMEOUT |
Request timeout in seconds | 10 |
Yes | Must be > 0 |
DEBUG |
Enable debug mode | true |
No | - |
INTERFACE |
Interface type | http |
Yes | http or cli |
SERVER_PORT |
HTTP server port | 8080 |
Conditional* | 1-65535 |
STORE |
Storage backend | inmemory |
Yes | inmemory or database |
DATABASE_URL |
MongoDB connection URL | - | Conditional** | - |
DATABASE_NAME |
MongoDB database name | - | Conditional** | - |
* Required when INTERFACE=http
** Required when STORE=database
For REST API with In-Memory Storage:
# .env
APP_NAME="Todos Service"
APP_VERSION="1.0.0"
INTERFACE="http"
SERVER_PORT=8080
STORE="inmemory"
DEBUG="false"For CLI with MongoDB Storage:
# .env
APP_NAME="Todos CLI"
INTERFACE="cli"
STORE="database"
DATABASE_URL="mongodb://localhost:27017"
DATABASE_NAME="todos"# Set configuration
export INTERFACE=http
export STORE=inmemory
export SERVER_PORT=8080
# Run the application
./build/go-todosThe API will be available at http://localhost:8080
# Set configuration
export INTERFACE=cli
export STORE=inmemory
# Run CLI commands
./build/go-todos [command] [flags]
# Example command
./build/go-todos list-todos
# To check available commands, use the help flag
./build/go-todos --help# Start MongoDB (if using Docker)
docker run -d -p 27017:27017 --name mongodb mongo:latest
# Configure application
export INTERFACE=http
export STORE=database
export DATABASE_URL="mongodb://localhost:27017"
export DATABASE_NAME="todos"
# Run the application
./build/go-todos.
├── cmd # Application entrypoint
│ └── main.go
├── Dockerfile
├── go.mod
├── go.sum
├── internal
│ ├── config # Configuration management
│ │ └── config.go
│ ├── core
│ │ ├── app # Business logic
│ │ │ ├── service.go
│ │ │ └── todos.go
│ │ ├── domain # Entities
│ │ │ └── models.go
│ │ └── ports # Rules of engagement
│ │ ├── app_repository.go
│ │ └── app_service.go
│ └── framework
│ ├── api # HTTP/REST adapter
│ │ ├── handlers.go
│ │ └── router.go
│ ├── cli # CLI adapter
│ │ ├── cmd.go
│ │ └── commands.go
│ ├── db # MongoDB adapter
│ │ ├── connect.go
│ │ ├── db.go
│ │ └── todos.go
│ └── store # In-Memory store adapter
│ └── store.go
├── LICENSE
├── Makefile
└── README.md
- Implement the
ports.AppRepositoryinterface - Add factory function to
storeFactoriesmap inmain.go - Update config validation if needed
- Add business logic to
internal/core/app/service.go - Update repository interface in
internal/core/ports/if storage changes needed - Implement interface methods in relevant adapters
- Add API endpoints or CLI commands as needed
docker build -t go-todos:latest .
docker run -p 8080:8080 \
-e INTERFACE=http \
-e STORE=inmemory \
go-todos:latest# Run all tests
go test ./...
# Run tests with coverage
go test -cover ./...
# Run tests for specific package
go test ./internal/core/app/...- Independence: Business logic is independent of frameworks, UI, and databases
- Testability: Core logic can be tested without external dependencies by using mock interfaces
- Flexibility: Easy to swap implementations (e.g., MongoDB → PostgreSQL)
- Maintainability: Clear boundaries between layers reduce coupling
Dependencies flow inward: Adapters depend on ports, ports depend on domain, but never the reverse.
Adapters → Ports → Domain Logic
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
- Hexagonal Architecture pattern by Alistair Cockburn
- Gin for REST API framework
- Cobra for CLI framework
- Viper for configuration management
- Zap for structured logging
- Go Playground Validator for data validation
Mohammed Mwijaa - @__mmwijaa__
Project Link - https://github.com/mwinyimoha/go-todos