{"id":37115461,"url":"https://github.com/abitofhelp/hybrid_app_go","last_synced_at":"2026-01-14T13:33:26.343Z","repository":{"id":326308451,"uuid":"1100480274","full_name":"abitofhelp/hybrid_app_go","owner":"abitofhelp","description":"Go Enterprise Starter with Hybrid DDD/Clean/Hexagonal Architecture","archived":false,"fork":false,"pushed_at":"2025-12-28T08:55:06.000Z","size":31412,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-12-30T17:29:26.341Z","etag":null,"topics":["clean-arch","ddd","generics","go","go-generics","golang","hexagonal","ports-and-adapters","result-monad","starter","static-dispatch","template"],"latest_commit_sha":null,"homepage":"https://pkg.go.dev/github.com/abitofhelp/hybrid_app_go","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/abitofhelp.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":"abitofhelp"}},"created_at":"2025-11-20T10:36:22.000Z","updated_at":"2025-12-28T08:55:09.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/abitofhelp/hybrid_app_go","commit_stats":null,"previous_names":["abitofhelp/hybrid_app_go"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/abitofhelp/hybrid_app_go","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/abitofhelp%2Fhybrid_app_go","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/abitofhelp%2Fhybrid_app_go/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/abitofhelp%2Fhybrid_app_go/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/abitofhelp%2Fhybrid_app_go/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/abitofhelp","download_url":"https://codeload.github.com/abitofhelp/hybrid_app_go/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/abitofhelp%2Fhybrid_app_go/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28421248,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T13:30:50.153Z","status":"ssl_error","status_checked_at":"2026-01-14T13:29:08.907Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["clean-arch","ddd","generics","go","go-generics","golang","hexagonal","ports-and-adapters","result-monad","starter","static-dispatch","template"],"created_at":"2026-01-14T13:33:25.704Z","updated_at":"2026-01-14T13:33:26.326Z","avatar_url":"https://github.com/abitofhelp.png","language":"Go","funding_links":["https://github.com/sponsors/abitofhelp"],"categories":[],"sub_categories":[],"readme":"# Enterprise Starter with Hybrid DDD/Clean/Hexagonal Architecture\n\n[![License](https://img.shields.io/badge/license-BSD--3--Clause-blue.svg)](LICENSE) [![Go](https://img.shields.io/badge/Go-1.23+-00ADD8.svg)](https://go.dev)\n\n**Version:** 1.0.1\u003cbr\u003e\n**Date:** 2025-11-29\u003cbr\u003e\n**SPDX-License-Identifier:** BSD-3-Clause\u003cbr\u003e\n**License File:** See the LICENSE file in the project root\u003cbr\u003e\n**Copyright:** © 2025 Michael Gardner, A Bit of Help, Inc.\u003cbr\u003e\n**Status:** Released\n\n## Overview\n\nA **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).\n\n\u003e **Starter Template:** This project serves as a **starter template for enterprise Go application development**. Use the included `scripts/brand_project/brand_project.py` script to generate a new project from this template with your own project name, module paths, and branding. See [Creating a New Project](#creating-a-new-project) below.\n\nThis is a **desktop/enterprise application template** showcasing:\n- **5-Layer Hexagonal Architecture** (Domain, Application, Infrastructure, Presentation, Bootstrap)\n- **Strict Module Boundaries** via go.work and separate go.mod per layer\n- **Static Dispatch via Generics** (zero-overhead dependency injection)\n- **Railway-Oriented Programming** with Result monads (no panics across boundaries)\n- **Presentation Isolation** pattern (only Domain is shareable across apps)\n- **Multi-Module Workspace** (compiler-enforced boundaries)\n\n## Getting Started\n\n### Clone with Submodules\n\nThis repository uses git submodules for shared tooling. Clone with:\n\n```bash\ngit clone --recurse-submodules https://github.com/abitofhelp/hybrid_app_go.git\n```\n\nOr if already cloned without submodules:\n\n```bash\ngit submodule update --init --recursive\n# Or: make submodule-init\n```\n\n## Features\n\n- ✅ Multi-module workspace structure with go.work\n- ✅ Custom domain Result/Option monads (ZERO external module dependencies)\n- ✅ Static dispatch via generics (zero-overhead DI)\n- ✅ Application.Error re-export pattern\n- ✅ Module boundary enforcement via go.mod\n- ✅ Context propagation for cancellation/timeout support\n- ✅ Panic recovery at infrastructure boundaries\n- ✅ Concurrency-ready patterns (documented, ready for extension)\n- ✅ Comprehensive Makefile automation\n\n## Architecture\n\n### Module Structure\n\n**Strict boundaries enforced by Go modules:**\n\n```\nhybrid_app_go/\n├── go.work                          # Workspace definition (manages all modules)\n├── domain/                          # Module: Pure business logic (ZERO external module dependencies)\n│   └── go.mod                       # ZERO external module dependencies - custom Result/Option types\n├── application/                     # Module: Use cases and ports\n│   └── go.mod                       # Depends ONLY on domain\n├── infrastructure/                  # Module: Driven adapters\n│   └── go.mod                       # Depends on application + domain\n├── presentation/                    # Module: Driving adapters (CLI)\n│   └── go.mod                       # Depends ONLY on application (NOT domain)\n├── bootstrap/                       # Module: Composition root\n│   └── go.mod                       # Depends on ALL modules\n└── cmd/greeter/                     # Module: Main entry point\n    └── go.mod                       # Depends only on bootstrap\n```\n\n### Key Architectural Rules\n\n**Critical Boundary Rule:**\n\u003e **Presentation is the ONLY outer layer prevented from direct Domain access**\n\n- ✅ **Infrastructure** CAN access `domain/*` (implements repositories, uses entities)\n- ✅ **Application** depends on `domain/*` (orchestrates domain logic)\n- ❌ **Presentation** CANNOT access `domain/*` (must use `application/error`, `application/model`, etc.)\n\n**Why This Matters:**\n- Domain is the **only shareable layer** across multiple applications\n- Each app has its own Application/Infrastructure/Presentation/Bootstrap\n- Prevents tight coupling between UI and business logic\n- Allows multiple UIs (CLI, REST, GUI) to share the same Domain\n\n**The Solution:** `application/error` re-exports `domain/error` types (zero overhead type aliases)\n\n### Dependency Injection Pattern\n\n**Go (Static Dispatch via Generics)**:\n```go\nimport (\n    \"context\"\n    domerr \"github.com/abitofhelp/hybrid_app_go/domain/error\"\n    \"github.com/abitofhelp/hybrid_app_go/application/model\"\n    \"github.com/abitofhelp/hybrid_app_go/application/port/outbound\"\n)\n\n// Port interface defines the contract\ntype WriterPort interface {\n    Write(ctx context.Context, message string) domerr.Result[model.Unit]\n}\n\n// Generic use case with interface constraint\ntype GreetUseCase[W outbound.WriterPort] struct {\n    writer W\n}\n\nfunc NewGreetUseCase[W outbound.WriterPort](writer W) *GreetUseCase[W] {\n    return \u0026GreetUseCase[W]{writer: writer}\n}\n\nfunc (uc *GreetUseCase[W]) Execute(ctx context.Context, cmd GreetCommand) domerr.Result[model.Unit] {\n    // uc.writer.Write() is statically dispatched - compiler knows exact type\n}\n```\n\n**Wiring in Bootstrap:**\n```go\n// Step 1: Create Infrastructure adapter (concrete type)\nconsoleWriter := adapter.NewConsoleWriter()\n\n// Step 2: Instantiate Use Case with concrete type parameter\ngreetUseCase := usecase.NewGreetUseCase[*adapter.ConsoleWriter](consoleWriter)\n\n// Step 3: Instantiate Command with concrete use case type\ngreetCommand := command.NewGreetCommand[*usecase.GreetUseCase[*adapter.ConsoleWriter]](greetUseCase)\n\n// Step 4: Run - all method calls are statically dispatched\nreturn greetCommand.Run(os.Args)\n```\n\n**Benefits:**\n- ✅ **Zero runtime overhead** (no vtable lookups, methods devirtualized)\n- ✅ **Type-safe** (verified at compile time)\n- ✅ **Static dispatch** (compiler knows exact types)\n- ✅ **Inlining potential** (optimizer can inline method calls)\n\n## Quick Start\n\n### Prerequisites\n\n- **Go 1.23+** (for workspace support)\n- **golangci-lint** (optional, for linting)\n\n### Building\n\n```bash\n# Build the project\nmake build\n\n# Clean artifacts\nmake clean\n\n# Rebuild from scratch\nmake rebuild\n```\n\n### Running\n\n```bash\n# Run the application\nmake run NAME=\"Alice\"\n\n# Or run directly\n./bin/greeter Alice\n```\n\n## Usage\n\n```bash\n# Greet a person\n./bin/greeter Alice\n# Output: Hello, Alice!\n\n# Name with spaces\n./bin/greeter \"Bob Smith\"\n# Output: Hello, Bob Smith!\n\n# No arguments (shows usage)\n./bin/greeter\n# Output: Usage: greeter \u003cname\u003e\n# Exit code: 1\n\n# Empty name (validation error)\n./bin/greeter \"\"\n# Output: Error: Person name cannot be empty\n# Exit code: 1\n```\n\n### Exit Codes\n\n- **0**: Success\n- **1**: Failure (validation error, infrastructure error, or missing arguments)\n\n## Testing\n\n```bash\n# Run all tests\nmake test\n\n# Run with coverage\nmake test-coverage\n\n# Unit tests only\nmake test-unit\n```\n\n**Test Structure:**\n- **Unit tests**: Co-located with code (`*_test.go`)\n- **Integration tests**: `test/integration/` with `//go:build integration` tag\n- **E2E tests**: `test/e2e/` with `//go:build e2e` tag\n\n## Documentation\n\n- 📚 **[Go Workspaces](https://go.dev/doc/tutorial/workspaces)** - Multi-module workspace tutorial\n- 🏗️ **[Hexagonal Architecture](https://alistair.cockburn.us/hexagonal-architecture/)** - Architecture pattern\n- 🚂 **[Railway-Oriented Programming](https://fsharpforfunandprofit.com/rop/)** - Error handling pattern\n\n## Code Standards\n\nThis project follows:\n- **Go Language Standards** (`~/.claude/agents/go.md`)\n- **Architecture Standards** (`~/.claude/agents/architecture.md`)\n- **Functional Programming Standards** (`~/.claude/agents/functional.md`)\n\n### Key Standards Applied\n\n1. **SPDX Headers:** All `.go` files have SPDX license headers\n2. **Result Monads:** All fallible operations return `domerr.Result[T]`\n3. **No Panics:** Errors are values, not thrown (recovery patterns for panic conversion)\n4. **Module Boundaries:** Compiler-enforced via go.mod\n5. **Static Dispatch:** Generic types with interface constraints for zero-overhead DI\n6. **Table-Driven Tests:** Using testify assertions (test module, NOT domain)\n\n## Creating a New Project\n\nThis 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:\n\n```bash\n# From the scripts directory\ncd scripts\npython3 -m brand_project \\\n    --old-project hybrid_app_go \\\n    --new-project my_awesome_app \\\n    --old-org abitofhelp \\\n    --new-org mycompany \\\n    --source /path/to/hybrid_app_go \\\n    --target /path/to/my_awesome_app\n```\n\n**What gets updated:**\n- Project name throughout all files\n- GitHub organization/username in module paths\n- Copyright holder information\n- All `go.mod` module paths\n- Import statements in Go source files\n- Documentation and README files\n\n## Submodule Management\n\nThis project uses git submodules for shared Python tooling:\n\n- `scripts/python` - Build, release, and architecture scripts\n- `test/python` - Shared test fixtures and configuration\n\n### Workflow\n\n```\nhybrid_python_scripts (source repo)\n         │\n         │ git push (manual)\n         ▼\n      GitHub\n         │\n         │ make submodule-update (in each consuming repo)\n         ▼\n┌─────────────────────────────────┐\n│  1. Pull new submodule commit   │\n│  2. Stage reference change      │\n│  3. Commit locally              │\n│  4. Push to remote              │\n└─────────────────────────────────┘\n```\n\n### Commands\n\n```bash\n# After fresh clone\nmake submodule-init\n\n# Pull latest from submodule repos\nmake submodule-update\n\n# Check current submodule commits\nmake submodule-status\n```\n\n### Bulk Update (all repositories)\n\n```bash\npython3 ~/Python/src/github.com/abitofhelp/git/update_submodules.py\n\n# Options:\n#   --dry-run   Show what would happen without changes\n#   --no-push   Update locally but do not push to remote\n```\n\n## Contributing\n\nThis project is not open to external contributions at this time.\n\n## AI Assistance \u0026 Authorship\n\nThis 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.\n\nWe use AI coding assistants (such as OpenAI GPT models and Anthropic Claude Code) as part of the development workflow to help with:\n\n- drafting and refactoring code and tests,\n- exploring design and implementation alternatives,\n- generating or refining documentation and examples,\n- and performing tedious and error-prone chores.\n\nAI 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.\n\n## License\n\nCopyright © 2025 Michael Gardner, A Bit of Help, Inc.\n\nLicensed under the BSD-3-Clause License. See [LICENSE](LICENSE) for details.\n\n## Author\n\nMichael Gardner\nA Bit of Help, Inc.\nhttps://github.com/abitofhelp\n\n## Project Status\n\n**Status**: Production Ready (v1.0.1)\n\n- ✅ Multi-module workspace structure with go.work\n- ✅ Custom domain Result/Option monads (ZERO external module dependencies)\n- ✅ Static dispatch via generics (zero-overhead DI)\n- ✅ Application.Error re-export pattern\n- ✅ Module boundary enforcement via go.mod\n- ✅ Comprehensive Makefile automation\n- ✅ All layers ported from Ada to Go\n- ✅ Functioning CLI application\n- ✅ Context propagation for cancellation/timeout support\n- ✅ Panic recovery at infrastructure boundaries\n- ✅ Concurrency-ready patterns (documented, ready for extension)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fabitofhelp%2Fhybrid_app_go","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fabitofhelp%2Fhybrid_app_go","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fabitofhelp%2Fhybrid_app_go/lists"}