# 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: 1. **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 2. **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/ ``` 3. **Service Independence**: Each service maintains: - Own `go.mod` module (or shared workspace) - Own internal implementation (`internal/` or `services/{service}/internal/`) - Own gRPC/HTTP API definitions - Own database schema and migrations - Own entry point in `cmd/{service-name}/` 4. **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 5. **Development Mode**: All services can run from same repository: - Single `go.work` workspace file (Go 1.18+) - Shared dependencies and versioning - Easy local development with `docker-compose` - Atomic changes across services when needed ## Rationale ### Why Monorepo? 1. **Code Sharing**: Core kernel (config, logger, DI) is shared across all services. Monorepo makes this easy. 2. **Atomic Changes**: Changes to shared interfaces can be updated across all services in a single commit. 3. **Simplified CI/CD**: Single CI pipeline can test all services and their interactions. 4. **Easier Development**: Developers can run all services locally from one repository. 5. **Version Consistency**: Shared dependencies (like core kernel) are always in sync. 6. **Cross-Service Refactoring**: Easier to refactor shared code across services. 7. **Unified Documentation**: All documentation in one place. ### Why Not Separate Repositories? 1. **Code Duplication**: Shared core kernel would need to be published as a library or duplicated. 2. **Version Management**: Core kernel changes would require coordinated releases across repositories. 3. **Development Complexity**: Developers would need to clone multiple repositories. 4. **CI/CD Complexity**: Multiple pipelines to manage and coordinate. 5. **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.work` for 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 1. **Repository Size**: Use Git LFS for large files, sparse checkout for large repos 2. **Permissions**: Use CODEOWNERS file and branch protection rules 3. **Build Time**: Use build caching and parallel builds in CI/CD 4. **Deployment**: Services are independently deployable despite shared repo 5. **Merge Conflicts**: Use feature branches, code review, and clear ownership 6. **Git History**: Use conventional commits and clear service prefixes ## Implementation Strategy ### Phase 1: Core Kernel (Epic 0-1) - Current - Core kernel in `internal/` and `pkg/` - 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 point - `cmd/identity-service/` - Identity Service entry point - `cmd/authz-service/` - Authz Service entry point - `cmd/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 - 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/` or `services/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 // 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: 1. Core kernel can be extracted to a shared library 2. Services can be moved to separate repos with git history 3. Service client interfaces remain in shared package 4. CI/CD can be adapted to multi-repo workflows ## References - [ADR-0007: Project Directory Structure](./0007-project-directory-structure.md) - [ADR-0029: Microservices Architecture](./0029-microservices-architecture.md) - [ADR-0030: Service Communication Strategy](./0030-service-communication-strategy.md) - [Go Workspaces](https://go.dev/doc/tutorial/workspaces) - [Monorepo vs. Multi-repo](https://www.atlassian.com/git/tutorials/monorepos) - [Google's Monorepo Strategy](https://cacm.acm.org/magazines/2016/7/204032-why-google-stores-billions-of-lines-of-code-in-a-single-repository/fulltext)