diff --git a/docs/content/adr/0031-service-repository-structure.md b/docs/content/adr/0031-service-repository-structure.md new file mode 100644 index 0000000..8aaeba8 --- /dev/null +++ b/docs/content/adr/0031-service-repository-structure.md @@ -0,0 +1,228 @@ +# 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 + │ ├── 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 module 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 2) +- Create service directories in `cmd/`: + - `cmd/auth-service/` + - `cmd/identity-service/` + - `cmd/authz-service/` + - `cmd/audit-service/` +- Create service implementations: + - Option A: `services/{service}/internal/` for each service + - Option B: `internal/{service}/` for each service (if keeping all in internal/) + - Option C: Service code directly in `cmd/{service}/` for simple services +- Define service client interfaces in `pkg/services/` +- 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 +│ ├── auth-service/ # Auth entry point +│ ├── identity-service/ # Identity entry point +│ └── ... +├── services/ # Service implementations +│ ├── auth/ +│ │ ├── internal/ # Service implementation +│ │ └── api/ # gRPC/HTTP definitions +│ └── ... +├── internal/ # Core kernel (shared) +├── pkg/ # Public interfaces +└── modules/ # Feature modules +``` + +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) + diff --git a/docs/content/adr/README.md b/docs/content/adr/README.md index c1218bd..ea9e3a6 100644 --- a/docs/content/adr/README.md +++ b/docs/content/adr/README.md @@ -73,6 +73,7 @@ Each ADR follows this structure: - [ADR-0029: Microservices Architecture](./0029-microservices-architecture.md) - Microservices architecture from day one - [ADR-0030: Service Communication Strategy](./0030-service-communication-strategy.md) - Service client abstraction and communication patterns +- [ADR-0031: Service Repository Structure](./0031-service-repository-structure.md) - Monorepo with service directories ## Adding New ADRs