Version: 1.0.1
Date: 2025-11-29
SPDX-License-Identifier: BSD-3-Clause
License File: See the LICENSE file in the project root
Copyright: Β© 2025 Michael Gardner, A Bit of Help, Inc.
Status: Released
A professional Go application demonstrating hybrid DDD/Clean/Hexagonal architecture with strict module boundaries enforced via Go workspaces and functional programming principles using custom domain-level Result/Option monads (ZERO external module dependencies in domain layer).
Starter Template: This project serves as a starter template for enterprise Go application development. Use the included
scripts/brand_project/brand_project.pyscript to generate a new project from this template with your own project name, module paths, and branding. See Creating a New Project below.
This is a desktop/enterprise application template showcasing:
- 5-Layer Hexagonal Architecture (Domain, Application, Infrastructure, Presentation, Bootstrap)
- Strict Module Boundaries via go.work and separate go.mod per layer
- Static Dispatch via Generics (zero-overhead dependency injection)
- Railway-Oriented Programming with Result monads (no panics across boundaries)
- Presentation Isolation pattern (only Domain is shareable across apps)
- Multi-Module Workspace (compiler-enforced boundaries)
This repository uses git submodules for shared tooling. Clone with:
git clone --recurse-submodules https://github.com/abitofhelp/hybrid_app_go.gitOr if already cloned without submodules:
git submodule update --init --recursive
# Or: make submodule-init- β Multi-module workspace structure with go.work
- β Custom domain Result/Option monads (ZERO external module dependencies)
- β Static dispatch via generics (zero-overhead DI)
- β Application.Error re-export pattern
- β Module boundary enforcement via go.mod
- β Context propagation for cancellation/timeout support
- β Panic recovery at infrastructure boundaries
- β Concurrency-ready patterns (documented, ready for extension)
- β Comprehensive Makefile automation
Strict boundaries enforced by Go modules:
hybrid_app_go/
βββ go.work # Workspace definition (manages all modules)
βββ domain/ # Module: Pure business logic (ZERO external module dependencies)
β βββ go.mod # ZERO external module dependencies - custom Result/Option types
βββ application/ # Module: Use cases and ports
β βββ go.mod # Depends ONLY on domain
βββ infrastructure/ # Module: Driven adapters
β βββ go.mod # Depends on application + domain
βββ presentation/ # Module: Driving adapters (CLI)
β βββ go.mod # Depends ONLY on application (NOT domain)
βββ bootstrap/ # Module: Composition root
β βββ go.mod # Depends on ALL modules
βββ cmd/greeter/ # Module: Main entry point
βββ go.mod # Depends only on bootstrap
Critical Boundary Rule:
Presentation is the ONLY outer layer prevented from direct Domain access
- β
Infrastructure CAN access
domain/*(implements repositories, uses entities) - β
Application depends on
domain/*(orchestrates domain logic) - β Presentation CANNOT access
domain/*(must useapplication/error,application/model, etc.)
Why This Matters:
- Domain is the only shareable layer across multiple applications
- Each app has its own Application/Infrastructure/Presentation/Bootstrap
- Prevents tight coupling between UI and business logic
- Allows multiple UIs (CLI, REST, GUI) to share the same Domain
The Solution: application/error re-exports domain/error types (zero overhead type aliases)
Go (Static Dispatch via Generics):
import (
"context"
domerr "github.com/abitofhelp/hybrid_app_go/domain/error"
"github.com/abitofhelp/hybrid_app_go/application/model"
"github.com/abitofhelp/hybrid_app_go/application/port/outbound"
)
// Port interface defines the contract
type WriterPort interface {
Write(ctx context.Context, message string) domerr.Result[model.Unit]
}
// Generic use case with interface constraint
type GreetUseCase[W outbound.WriterPort] struct {
writer W
}
func NewGreetUseCase[W outbound.WriterPort](writer W) *GreetUseCase[W] {
return &GreetUseCase[W]{writer: writer}
}
func (uc *GreetUseCase[W]) Execute(ctx context.Context, cmd GreetCommand) domerr.Result[model.Unit] {
// uc.writer.Write() is statically dispatched - compiler knows exact type
}Wiring in Bootstrap:
// Step 1: Create Infrastructure adapter (concrete type)
consoleWriter := adapter.NewConsoleWriter()
// Step 2: Instantiate Use Case with concrete type parameter
greetUseCase := usecase.NewGreetUseCase[*adapter.ConsoleWriter](consoleWriter)
// Step 3: Instantiate Command with concrete use case type
greetCommand := command.NewGreetCommand[*usecase.GreetUseCase[*adapter.ConsoleWriter]](greetUseCase)
// Step 4: Run - all method calls are statically dispatched
return greetCommand.Run(os.Args)Benefits:
- β Zero runtime overhead (no vtable lookups, methods devirtualized)
- β Type-safe (verified at compile time)
- β Static dispatch (compiler knows exact types)
- β Inlining potential (optimizer can inline method calls)
- Go 1.23+ (for workspace support)
- golangci-lint (optional, for linting)
# Build the project
make build
# Clean artifacts
make clean
# Rebuild from scratch
make rebuild# Run the application
make run NAME="Alice"
# Or run directly
./bin/greeter Alice# Greet a person
./bin/greeter Alice
# Output: Hello, Alice!
# Name with spaces
./bin/greeter "Bob Smith"
# Output: Hello, Bob Smith!
# No arguments (shows usage)
./bin/greeter
# Output: Usage: greeter <name>
# Exit code: 1
# Empty name (validation error)
./bin/greeter ""
# Output: Error: Person name cannot be empty
# Exit code: 1- 0: Success
- 1: Failure (validation error, infrastructure error, or missing arguments)
# Run all tests
make test
# Run with coverage
make test-coverage
# Unit tests only
make test-unitTest Structure:
- Unit tests: Co-located with code (
*_test.go) - Integration tests:
test/integration/with//go:build integrationtag - E2E tests:
test/e2e/with//go:build e2etag
- π Go Workspaces - Multi-module workspace tutorial
- ποΈ Hexagonal Architecture - Architecture pattern
- π Railway-Oriented Programming - Error handling pattern
This project follows:
- Go Language Standards (
~/.claude/agents/go.md) - Architecture Standards (
~/.claude/agents/architecture.md) - Functional Programming Standards (
~/.claude/agents/functional.md)
- SPDX Headers: All
.gofiles have SPDX license headers - Result Monads: All fallible operations return
domerr.Result[T] - No Panics: Errors are values, not thrown (recovery patterns for panic conversion)
- Module Boundaries: Compiler-enforced via go.mod
- Static Dispatch: Generic types with interface constraints for zero-overhead DI
- Table-Driven Tests: Using testify assertions (test module, NOT domain)
This repository serves as a starter template for enterprise Go applications. Use the brand_project.py script to create a new project with your own branding:
# From the scripts directory
cd scripts
python3 -m brand_project \
--old-project hybrid_app_go \
--new-project my_awesome_app \
--old-org abitofhelp \
--new-org mycompany \
--source /path/to/hybrid_app_go \
--target /path/to/my_awesome_appWhat gets updated:
- Project name throughout all files
- GitHub organization/username in module paths
- Copyright holder information
- All
go.modmodule paths - Import statements in Go source files
- Documentation and README files
This project uses git submodules for shared Python tooling:
scripts/python- Build, release, and architecture scriptstest/python- Shared test fixtures and configuration
hybrid_python_scripts (source repo)
β
β git push (manual)
βΌ
GitHub
β
β make submodule-update (in each consuming repo)
βΌ
βββββββββββββββββββββββββββββββββββ
β 1. Pull new submodule commit β
β 2. Stage reference change β
β 3. Commit locally β
β 4. Push to remote β
βββββββββββββββββββββββββββββββββββ
# After fresh clone
make submodule-init
# Pull latest from submodule repos
make submodule-update
# Check current submodule commits
make submodule-statuspython3 ~/Python/src/github.com/abitofhelp/git/update_submodules.py
# Options:
# --dry-run Show what would happen without changes
# --no-push Update locally but do not push to remoteThis project is not open to external contributions at this time.
This project β including its source code, tests, documentation, and other deliverables β is designed, implemented, and maintained by human developers, with Michael Gardner as the Principal Software Engineer and project lead.
We use AI coding assistants (such as OpenAI GPT models and Anthropic Claude Code) as part of the development workflow to help with:
- drafting and refactoring code and tests,
- exploring design and implementation alternatives,
- generating or refining documentation and examples,
- and performing tedious and error-prone chores.
AI systems are treated as tools, not authors. All changes are reviewed, adapted, and integrated by the human maintainers, who remain fully responsible for the architecture, correctness, and licensing of this project.
Copyright Β© 2025 Michael Gardner, A Bit of Help, Inc.
Licensed under the BSD-3-Clause License. See LICENSE for details.
Michael Gardner A Bit of Help, Inc. https://github.com/abitofhelp
Status: Production Ready (v1.0.1)
- β Multi-module workspace structure with go.work
- β Custom domain Result/Option monads (ZERO external module dependencies)
- β Static dispatch via generics (zero-overhead DI)
- β Application.Error re-export pattern
- β Module boundary enforcement via go.mod
- β Comprehensive Makefile automation
- β All layers ported from Ada to Go
- β Functioning CLI application
- β Context propagation for cancellation/timeout support
- β Panic recovery at infrastructure boundaries
- β Concurrency-ready patterns (documented, ready for extension)