docs: add ADR-0031 for service repository structure decision
All checks were successful
CI / Test (push) Successful in 11s
CI / Format Check (push) Successful in 3s
CI / Lint (push) Successful in 9s
CI / Build (push) Successful in 6s

Create ADR documenting the decision to use a monorepo structure with
service directories for organizing microservices (Option 1).

The ADR covers:
- Decision to use monorepo with service directories
- Rationale for monorepo vs separate repositories
- Proposed directory structure with cmd/ and services/
- Implementation strategy across epics
- Consequences and mitigations
- Migration path if needed in the future

This clarifies how services (Auth, Identity, Authz, Audit) should be
organized in the codebase while maintaining service independence and
microservices architecture principles.
This commit is contained in:
2025-11-05 15:13:34 +01:00
parent 7cadc3b3c0
commit a38a08ca17
2 changed files with 229 additions and 0 deletions

View File

@@ -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)

View File

@@ -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