docs: add ADR-0031 for service repository structure decision
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:
228
docs/content/adr/0031-service-repository-structure.md
Normal file
228
docs/content/adr/0031-service-repository-structure.md
Normal 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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user