245 lines
9.8 KiB
Markdown
245 lines
9.8 KiB
Markdown
# 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)
|
|
|