Files
goplt/docs/content/adr/0031-service-repository-structure.md
0x1d 38a251968c docs: Align documentation with true microservices architecture
Transform all documentation from modular monolith to true microservices
architecture where core services are independently deployable.

Key Changes:
- Core Kernel: Infrastructure only (no business logic)
- Core Services: Auth, Identity, Authz, Audit as separate microservices
  - Each service has own entry point (cmd/{service}/)
  - Each service has own gRPC server and database schema
  - Services register with Consul for service discovery
- API Gateway: Moved from Epic 8 to Epic 1 as core infrastructure
  - Single entry point for all external traffic
  - Handles routing, JWT validation, rate limiting, CORS
- Service Discovery: Consul as primary mechanism (ADR-0033)
- Database Pattern: Per-service connections with schema isolation

Documentation Updates:
- Updated all 9 architecture documents
- Updated 4 ADRs and created 2 new ADRs (API Gateway, Service Discovery)
- Rewrote Epic 1: Core Kernel & Infrastructure (infrastructure only)
- Rewrote Epic 2: Core Services (Auth, Identity, Authz, Audit as services)
- Updated Epic 3-8 stories for service architecture
- Updated plan.md, playbook.md, requirements.md, index.md
- Updated all epic READMEs and story files

New ADRs:
- ADR-0032: API Gateway Strategy
- ADR-0033: Service Discovery Implementation (Consul)

New Stories:
- Epic 1.7: Service Client Interfaces
- Epic 1.8: API Gateway Implementation
2025-11-06 08:54:19 +01:00

9.8 KiB

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