Transform all documentation from modular monolith to true microservices
architecture where core services are independently deployable.
Key Changes:
- Core Kernel: Infrastructure only (no business logic)
- Core Services: Auth, Identity, Authz, Audit as separate microservices
- Each service has own entry point (cmd/{service}/)
- Each service has own gRPC server and database schema
- Services register with Consul for service discovery
- API Gateway: Moved from Epic 8 to Epic 1 as core infrastructure
- Single entry point for all external traffic
- Handles routing, JWT validation, rate limiting, CORS
- Service Discovery: Consul as primary mechanism (ADR-0033)
- Database Pattern: Per-service connections with schema isolation
Documentation Updates:
- Updated all 9 architecture documents
- Updated 4 ADRs and created 2 new ADRs (API Gateway, Service Discovery)
- Rewrote Epic 1: Core Kernel & Infrastructure (infrastructure only)
- Rewrote Epic 2: Core Services (Auth, Identity, Authz, Audit as services)
- Updated Epic 3-8 stories for service architecture
- Updated plan.md, playbook.md, requirements.md, index.md
- Updated all epic READMEs and story files
New ADRs:
- ADR-0032: API Gateway Strategy
- ADR-0033: Service Discovery Implementation (Consul)
New Stories:
- Epic 1.7: Service Client Interfaces
- Epic 1.8: API Gateway Implementation
9.8 KiB
ADR-0031: Service Repository Structure
Status
Accepted
Context
The platform follows a microservices architecture where each service (Auth, Identity, Authz, Audit, and feature modules) is an independent service. We need to decide how to organize these services in the codebase:
- Option 1: Monorepo with service directories (all services in one repository)
- Option 2: Separate repositories (each service in its own repository)
The decision affects:
- Code sharing and dependencies
- Development workflow
- CI/CD complexity
- Team autonomy and ownership
- Deployment strategies
- Code discoverability and navigation
Decision
Use a monorepo structure with service directories for all services:
-
Repository Structure: Single repository (
goplt/) containing:- Core Kernel (shared infrastructure)
- All core services (Auth, Identity, Authz, Audit)
- All feature modules (Blog, Billing, etc.)
- Shared interfaces and utilities
-
Service Organization: Each service lives in
cmd/directory:goplt/ ├── cmd/ │ ├── platform/ # Core kernel entry point (minimal, infrastructure only) │ ├── api-gateway/ # API Gateway service entry point │ ├── auth-service/ # Auth Service entry point │ ├── identity-service/ # Identity Service entry point │ ├── authz-service/ # Authz Service entry point │ ├── audit-service/ # Audit Service entry point │ └── blog-service/ # Blog feature service entry point ├── services/ # Service implementations (optional alternative) │ ├── auth/ │ │ ├── internal/ # Service implementation │ │ └── api/ # gRPC/HTTP definitions │ ├── identity/ │ ├── authz/ │ └── audit/ ├── internal/ # Core kernel implementation (shared) │ ├── config/ │ ├── logger/ │ └── di/ ├── pkg/ # Public interfaces │ ├── config/ # ConfigProvider interface │ ├── logger/ # Logger interface │ ├── services/ # Service client interfaces │ └── module/ # IModule interface └── modules/ # Feature modules (optional structure) └── blog/ -
Service Independence: Each service maintains:
- Own
go.modmodule (or shared workspace) - Own internal implementation (
internal/orservices/{service}/internal/) - Own gRPC/HTTP API definitions
- Own database schema and migrations
- Own entry point in
cmd/{service-name}/
- Own
-
Shared Code: Core kernel infrastructure shared across all services:
- Configuration management (
internal/config/,pkg/config/) - Logging system (
internal/logger/,pkg/logger/) - Dependency injection (
internal/di/) - Service client implementations (
internal/services/) - Common utilities and interfaces
- Configuration management (
-
Development Mode: All services can run from same repository:
- Single
go.workworkspace file (Go 1.18+) - Shared dependencies and versioning
- Easy local development with
docker-compose - Atomic changes across services when needed
- Single
Rationale
Why Monorepo?
-
Code Sharing: Core kernel (config, logger, DI) is shared across all services. Monorepo makes this easy.
-
Atomic Changes: Changes to shared interfaces can be updated across all services in a single commit.
-
Simplified CI/CD: Single CI pipeline can test all services and their interactions.
-
Easier Development: Developers can run all services locally from one repository.
-
Version Consistency: Shared dependencies (like core kernel) are always in sync.
-
Cross-Service Refactoring: Easier to refactor shared code across services.
-
Unified Documentation: All documentation in one place.
Why Not Separate Repositories?
-
Code Duplication: Shared core kernel would need to be published as a library or duplicated.
-
Version Management: Core kernel changes would require coordinated releases across repositories.
-
Development Complexity: Developers would need to clone multiple repositories.
-
CI/CD Complexity: Multiple pipelines to manage and coordinate.
-
Breaking Changes: Harder to coordinate breaking changes to shared interfaces.
Consequences
Positive
- Simplified Development: Single repository to clone and work with
- Code Reuse: Easy sharing of core kernel and utilities
- Atomic Changes: Update shared interfaces and all services together
- Consistent Dependencies: All services use same versions of core kernel
- Unified CI/CD: Single pipeline for all services
- Easier Onboarding: New developers work with one repository
- Cross-Service Testing: Can test service interactions easily
- Shared Documentation: All docs in one place
- Go Workspace Support: Can use
go.workfor multi-module development
Negative
- Repository Size: Repository grows as more services are added
- Permission Granularity: Harder to restrict access to specific services (Git permissions)
- Build Time: CI/CD may take longer as it builds all services
- Deployment Coupling: Services are in same repo (but can still deploy independently)
- Merge Conflicts: More likely with multiple teams working on same repo
- Git History: History can become cluttered with changes from many services
Mitigations
- Repository Size: Use Git LFS for large files, sparse checkout for large repos
- Permissions: Use CODEOWNERS file and branch protection rules
- Build Time: Use build caching and parallel builds in CI/CD
- Deployment: Services are independently deployable despite shared repo
- Merge Conflicts: Use feature branches, code review, and clear ownership
- Git History: Use conventional commits and clear service prefixes
Implementation Strategy
Phase 1: Core Kernel (Epic 0-1) - Current
- Core kernel in
internal/andpkg/ - Single entry point
cmd/platform/ - Shared infrastructure established
Phase 2: Service Structure (Epic 1-2)
- Epic 1: Create API Gateway service:
cmd/api-gateway/- API Gateway entry point- Service discovery integration
- Request routing to backend services
- Epic 2: Create core service directories:
cmd/auth-service/- Auth Service entry pointcmd/identity-service/- Identity Service entry pointcmd/authz-service/- Authz Service entry pointcmd/audit-service/- Audit Service entry point
- Create service implementations:
- Option A:
services/{service}/internal/for each service (recommended) - Option B:
internal/{service}/for each service - Each service has its own database connection pool
- Option A:
- Define service client interfaces in
pkg/services/:AuthServiceClient,IdentityServiceClient,AuthzServiceClient,AuditServiceClient
- Implement gRPC/HTTP clients in
internal/services/
Phase 3: Module Services (Epic 4+)
- Feature modules follow same pattern:
cmd/blog-service/for blog module- Module implementation in
modules/blog/orservices/blog/
Directory Structure Decision
Recommended: Use cmd/{service}/ for entry points and services/{service}/ for implementations:
goplt/
├── cmd/
│ ├── platform/ # Core kernel (minimal, infrastructure only)
│ ├── api-gateway/ # API Gateway entry point
│ ├── auth-service/ # Auth entry point
│ ├── identity-service/ # Identity entry point
│ ├── authz-service/ # Authz entry point
│ ├── audit-service/ # Audit entry point
│ └── blog-service/ # Blog feature service entry point
├── services/ # Service implementations
│ ├── gateway/
│ │ ├── internal/ # Gateway implementation
│ │ └── api/ # Routing logic
│ ├── auth/
│ │ ├── internal/ # Service implementation
│ │ └── api/ # gRPC/HTTP definitions
│ ├── identity/
│ ├── authz/
│ ├── audit/
│ └── blog/
├── internal/ # Core kernel (shared infrastructure)
├── pkg/ # Public interfaces
└── modules/ # Feature modules (optional structure)
This provides:
- Clear separation between entry points and implementations
- Easy to find service entry points
- Service code is organized together
- Consistent with Go best practices
Alternative: Go Workspace (Go 1.18+)
For maximum service independence while maintaining monorepo benefits:
// go.work
go 1.24
use (
. # Core kernel
./services/auth # Auth service module
./services/identity # Identity service module
./cmd/auth-service # Auth service entry
./cmd/identity-service # Identity service entry
)
Each service can have its own go.mod while sharing the workspace.
Migration Path
If we later need to split into separate repositories:
- Core kernel can be extracted to a shared library
- Services can be moved to separate repos with git history
- Service client interfaces remain in shared package
- CI/CD can be adapted to multi-repo workflows