Files
goplt/docs/content/architecture/architecture-modules.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

17 KiB

Module Architecture

This document details the architecture of modules (feature services), how they are structured as independent services, how they interact with core services, and how multiple services work together in the microservices architecture.

Table of Contents

Module Structure

Every module follows a consistent structure that separates concerns and enables clean integration with the platform.

graph TD
    subgraph "Module Structure"
        Manifest[module.yaml<br/>Manifest]
        
        subgraph "Public API (pkg/)"
            ModuleInterface[IModule Interface]
            ModuleTypes[Public Types]
        end
        
        subgraph "Internal Implementation (internal/)"
            API[API Handlers]
            Service[Domain Services]
            Repo[Repositories]
            Domain[Domain Models]
        end
        
        subgraph "Database Schema"
            EntSchema[Ent Schemas]
            Migrations[Migrations]
        end
    end
    
    Manifest --> ModuleInterface
    ModuleInterface --> API
    API --> Service
    Service --> Repo
    Repo --> Domain
    Repo --> EntSchema
    EntSchema --> Migrations
    
    style Manifest fill:#4a90e2,stroke:#2e5c8a,stroke-width:3px,color:#fff
    style ModuleInterface fill:#50c878,stroke:#2e7d4e,stroke-width:2px,color:#fff
    style Service fill:#7b68ee,stroke:#5a4fcf,stroke-width:2px,color:#fff

Service Directory Structure

Each module is an independent service with its own entry point:

cmd/blog-service/
└── main.go                   # Service entry point

services/blog/
├── go.mod                    # Service dependencies
├── module.yaml               # Service manifest
├── api/
│   └── blog.proto           # gRPC service definition
├── internal/
│   ├── api/
│   │   └── handler.go       # gRPC handlers
│   ├── domain/
│   │   ├── post.go          # Domain entities
│   │   └── post_repo.go     # Repository interface
│   ├── service/
│   │   └── post_service.go  # Business logic
│   └── database/
│       └── client.go        # Database connection
└── ent/
    ├── schema/
    │   └── post.go          # Ent schema
    └── migrate/             # Migrations

Module Interface

All modules must implement the IModule interface to integrate with the platform.

classDiagram
    class IModule {
        <<interface>>
        +Name() string
        +Version() string
        +Dependencies() []string
        +Init() fx.Option
        +Migrations() []MigrationFunc
        +OnStart(ctx) error
        +OnStop(ctx) error
    }
    
    class BlogModule {
        +Name() string
        +Version() string
        +Dependencies() []string
        +Init() fx.Option
        +Migrations() []MigrationFunc
    }
    
    class BillingModule {
        +Name() string
        +Version() string
        +Dependencies() []string
        +Init() fx.Option
        +Migrations() []MigrationFunc
    }
    
    IModule <|.. BlogModule
    IModule <|.. BillingModule

IModule Interface

type IModule interface {
    // Name returns a unique, human-readable identifier
    Name() string
    
    // Version returns the module version (semantic versioning)
    Version() string
    
    // Dependencies returns list of required modules (e.g., ["core >= 1.0.0"])
    Dependencies() []string
    
    // Init returns fx.Option that registers all module services
    Init() fx.Option
    
    // Migrations returns database migration functions
    Migrations() []func(*ent.Client) error
    
    // OnStart is called during application startup (optional)
    OnStart(ctx context.Context) error
    
    // OnStop is called during graceful shutdown (optional)
    OnStop(ctx context.Context) error
}

Module Lifecycle

Modules go through a well-defined lifecycle from discovery to shutdown.

stateDiagram-v2
    [*] --> Discovered: Module found
    Discovered --> Validated: Check dependencies
    Validated --> Loaded: Load module
    Loaded --> Initialized: Call Init()
    Initialized --> Migrated: Run migrations
    Migrated --> Started: Call OnStart()
    Started --> Running: Module active
    Running --> Stopping: Shutdown signal
    Stopping --> Stopped: Call OnStop()
    Stopped --> [*]
    
    Validated --> Rejected: Dependency check fails
    Rejected --> [*]

Module Initialization Sequence

sequenceDiagram
    participant Main
    participant Loader
    participant Registry
    participant Module
    participant DI
    participant Router
    participant DB
    participant Scheduler
    
    Main->>Loader: DiscoverModules()
    Loader->>Registry: Scan for modules
    Registry-->>Loader: Module list
    
    loop For each module
        Loader->>Module: Load module
        Module->>Registry: Register module
        Registry->>Registry: Validate dependencies
    end
    
    Main->>Registry: GetAllModules()
    Registry->>Registry: Resolve dependencies (topological sort)
    Registry-->>Main: Ordered module list
    
    Main->>DI: Create fx container
    
    loop For each module (in dependency order)
        Main->>Module: Init()
        Module->>DI: fx.Provide(services)
        Module->>Router: Register routes
        Module->>Scheduler: Register jobs
        Module->>DB: Register migrations
    end
    
    Main->>DB: Run migrations (core first)
    Main->>DI: Start container
    Main->>Module: OnStart() (optional)
    Main->>Router: Start HTTP server

Module Dependencies

Modules can depend on other modules, creating a dependency graph that must be resolved.

graph TD
    Core[Core Kernel]
    Blog[Blog Module]
    Billing[Billing Module]
    Analytics[Analytics Module]
    Notifications[Notification Module]
    
    Blog --> Core
    Billing --> Core
    Analytics --> Core
    Notifications --> Core
    
    Analytics --> Blog
    Analytics --> Billing
    Billing --> Blog
    Notifications --> Blog
    Notifications --> Billing
    
    style Core fill:#4a90e2,stroke:#2e5c8a,stroke-width:3px,color:#fff
    style Blog fill:#7b68ee,stroke:#5a4fcf,stroke-width:2px,color:#fff
    style Billing fill:#7b68ee,stroke:#5a4fcf,stroke-width:2px,color:#fff

Dependency Resolution

graph LR
    subgraph "Module Dependency Graph"
        M1[Module A<br/>depends on: Core]
        M2[Module B<br/>depends on: Core, Module A]
        M3[Module C<br/>depends on: Core, Module B]
        Core[Core Kernel]
    end
    
    subgraph "Resolved Load Order"
        Step1[1. Core Kernel]
        Step2[2. Module A]
        Step3[3. Module B]
        Step4[4. Module C]
    end
    
    Core --> M1
    M1 --> M2
    M2 --> M3
    
    Step1 --> Step2
    Step2 --> Step3
    Step3 --> Step4
    
    style Core fill:#4a90e2,stroke:#2e5c8a,stroke-width:3px,color:#fff
    style Step1 fill:#50c878,stroke:#2e7d4e,stroke-width:2px,color:#fff

Service Communication

Modules are implemented as independent services that communicate through service client interfaces. All inter-service communication uses gRPC (primary) or HTTP (fallback) via service clients. Services discover each other through the service registry (Consul).

Communication Patterns

graph TB
    subgraph "Communication Patterns"
        ServiceClients[Service Clients<br/>gRPC/HTTP]
        Events[Event Bus<br/>Kafka]
        Shared[Shared Infrastructure<br/>Redis, PostgreSQL]
    end
    
    subgraph "Blog Service"
        BlogService[Blog Service]
        BlogHandler[Blog Handler]
    end
    
    subgraph "Service Clients"
        AuthClient[Auth Service Client]
        IdentityClient[Identity Service Client]
        AuthzClient[Authz Service Client]
    end
    
    subgraph "Core Services"
        EventBus[Event Bus]
        AuthService[Auth Service<br/>:8081]
        IdentityService[Identity Service<br/>:8082]
    end
    
    subgraph "Analytics Service"
        AnalyticsService[Analytics Service]
    end
    
    BlogHandler --> BlogService
    BlogService -->|gRPC| AuthClient
    BlogService -->|gRPC| IdentityClient
    BlogService -->|gRPC| AuthzClient
    BlogService -->|gRPC| AuditClient
    BlogService -->|Publish| EventBus
    EventBus -->|Subscribe| AnalyticsService
    
    AuthClient -->|Discover| Registry
    IdentityClient -->|Discover| Registry
    AuthzClient -->|Discover| Registry
    AuditClient -->|Discover| Registry
    
    Registry --> AuthService
    Registry --> IdentityService
    Registry --> AuthzService
    Registry --> AuditService
    
    AuthClient --> AuthService
    IdentityClient --> IdentityService
    AuthzClient --> AuthzService
    AuditClient --> AuditService
    
    style EventBus fill:#4a90e2,stroke:#2e5c8a,stroke-width:3px,color:#fff
    style Registry fill:#50c878,stroke:#2e7d4e,stroke-width:3px,color:#fff
    style BlogService fill:#7b68ee,stroke:#5a4fcf,stroke-width:2px,color:#fff
    style AnalyticsService fill:#7b68ee,stroke:#5a4fcf,stroke-width:2px,color:#fff
    style ServiceClients fill:#50c878,stroke:#2e7d4e,stroke-width:2px,color:#fff

Event-Driven Communication

sequenceDiagram
    participant BlogModule
    participant EventBus
    participant AnalyticsModule
    participant NotificationModule
    participant AuditModule
    
    BlogModule->>EventBus: Publish("blog.post.created", event)
    EventBus->>AnalyticsModule: Deliver event
    EventBus->>NotificationModule: Deliver event
    EventBus->>AuditModule: Deliver event
    
    AnalyticsModule->>AnalyticsModule: Track post creation
    NotificationModule->>NotificationModule: Send notification
    AuditModule->>AuditModule: Log audit entry

Module Data Isolation

Modules can have their own database tables while sharing core tables.

erDiagram
    USERS ||--o{ USER_ROLES : has
    ROLES ||--o{ USER_ROLES : assigned_to
    ROLES ||--o{ ROLE_PERMISSIONS : has
    PERMISSIONS ||--o{ ROLE_PERMISSIONS : assigned_to
    
    BLOG_POSTS {
        string id PK
        string author_id FK
        string title
        string content
        timestamp created_at
    }
    
    BILLING_SUBSCRIPTIONS {
        string id PK
        string user_id FK
        string plan
        timestamp expires_at
    }
    
    USERS ||--o{ BLOG_POSTS : creates
    USERS ||--o{ BILLING_SUBSCRIPTIONS : subscribes
    
    AUDIT_LOGS {
        string id PK
        string actor_id
        string action
        string target_id
        jsonb metadata
    }
    
    USERS ||--o{ AUDIT_LOGS : performs

Multi-Tenancy Data Isolation

graph TB
    subgraph "Single Database"
        subgraph "Core Tables"
            Users[users<br/>tenant_id]
            Roles[roles<br/>tenant_id]
        end
        
        subgraph "Blog Module Tables"
            Posts[blog_posts<br/>tenant_id]
            Comments[blog_comments<br/>tenant_id]
        end
        
        subgraph "Billing Module Tables"
            Subscriptions[billing_subscriptions<br/>tenant_id]
            Invoices[billing_invoices<br/>tenant_id]
        end
    end
    
    subgraph "Query Filtering"
        EntInterceptor[Ent Interceptor]
        TenantFilter[WHERE tenant_id = ?]
    end
    
    Users --> EntInterceptor
    Posts --> EntInterceptor
    Subscriptions --> EntInterceptor
    EntInterceptor --> TenantFilter
    
    style EntInterceptor fill:#4a90e2,stroke:#2e5c8a,stroke-width:3px,color:#fff

Module Examples

Example: Blog Module

graph TB
    subgraph "Blog Module"
        BlogHandler[Blog Handler<br/>/api/v1/blog/posts]
        BlogService[Post Service]
        PostRepo[Post Repository]
        PostEntity[Post Entity]
    end
    
    subgraph "Service Clients"
        AuthClient[Auth Service Client<br/>gRPC]
        AuthzClient[Authz Service Client<br/>gRPC]
        IdentityClient[Identity Service Client<br/>gRPC]
        AuditClient[Audit Service Client<br/>gRPC]
    end
    
    subgraph "Core Services"
        EventBus[Event Bus<br/>Kafka]
        CacheService[Cache Service<br/>Redis]
    end
    
    subgraph "Database"
        PostsTable[(blog_posts)]
    end
    
    BlogHandler --> BlogService
    
    BlogService -->|gRPC| AuthClient
    BlogService -->|gRPC| AuthzClient
    BlogService -->|gRPC| IdentityClient
    BlogService -->|gRPC| AuditClient
    BlogService --> PostRepo
    BlogService --> EventBus
    BlogService --> CacheService
    
    PostRepo --> PostsTable
    PostRepo --> PostEntity
    
    style BlogModule fill:#7b68ee,stroke:#5a4fcf,stroke-width:2px,color:#fff
    style AuthService fill:#4a90e2,stroke:#2e5c8a,stroke-width:2px,color:#fff

Module Integration Example

graph LR
    subgraph "Request Flow"
        Request[HTTP Request<br/>POST /api/v1/blog/posts]
        Auth[Auth Middleware]
        Authz[Authz Middleware]
        Handler[Blog Handler]
        Service[Blog Service]
        Repo[Blog Repository]
        DB[(Database)]
    end
    
    subgraph "Service Clients"
        AuthClient[Auth Service Client]
        IdentityClient[Identity Service Client]
        AuthzClient[Authz Service Client]
    end
    
    subgraph "Side Effects"
        EventBus[Event Bus]
        AuditClient[Audit Service Client]
        Cache[Cache]
    end
    
    Request --> Auth
    Auth --> Authz
    Authz --> Handler
    Handler --> Service
    Service --> Repo
    Repo --> DB
    
    Service -->|gRPC| AuthClient
    Service -->|gRPC| IdentityClient
    Service -->|gRPC| AuthzClient
    Service -->|gRPC| AuditClient
    Service --> EventBus
    Service --> Cache
    
    style Request fill:#4a90e2,stroke:#2e5c8a,stroke-width:2px,color:#fff
    style Service fill:#7b68ee,stroke:#5a4fcf,stroke-width:2px,color:#fff
    style ServiceClients fill:#50c878,stroke:#2e7d4e,stroke-width:2px,color:#fff

Module Registration Flow

flowchart TD
    Start([Application Start]) --> LoadManifests["Load module.yaml files"]
    LoadManifests --> ValidateDeps["Validate dependencies"]
    ValidateDeps -->|Valid| SortModules["Topological sort modules"]
    ValidateDeps -->|Invalid| Error([Error: Missing dependencies])
    
    SortModules --> CreateDI["Create DI container"]
    CreateDI --> RegisterCore["Register core services"]
    
    RegisterCore --> LoopModules{"More modules?"}
    LoopModules -->|Yes| LoadModule["Load module"]
    LoadModule --> CallInit["Call module.Init()"]
    CallInit --> RegisterServices["Register module services"]
    RegisterServices --> RegisterRoutes["Register module routes"]
    RegisterRoutes --> RegisterJobs["Register module jobs"]
    RegisterJobs --> RegisterMigrations["Register module migrations"]
    RegisterMigrations --> LoopModules
    
    LoopModules -->|No| RunMigrations["Run all migrations"]
    RunMigrations --> StartModules["Call OnStart() for each module"]
    StartModules --> StartServer["Start HTTP server"]
    StartServer --> Running([Application Running])
    
    Running --> Shutdown([Shutdown Signal])
    Shutdown --> StopServer["Stop HTTP server"]
    StopServer --> StopModules["Call OnStop() for each module"]
    StopModules --> Cleanup["Cleanup resources"]
    Cleanup --> End([Application Stopped])
    
    style Start fill:#50c878,stroke:#2e7d4e,stroke-width:2px,color:#fff
    style Running fill:#4a90e2,stroke:#2e5c8a,stroke-width:2px,color:#fff
    style Error fill:#ff6b6b,stroke:#c92a2a,stroke-width:2px,color:#fff

Module Permissions Integration

Modules declare permissions that are automatically integrated into the permission system.

graph TB
    subgraph "Permission Generation"
        Manifest["module.yaml<br/>permissions: array"]
        Generator["Permission Generator"]
        GeneratedCode["pkg/perm/generated.go"]
    end
    
    subgraph "Permission Resolution"
        Request["HTTP Request"]
        AuthzMiddleware["Authz Middleware"]
        PermissionResolver["Permission Resolver"]
        UserRoles["User Roles"]
        RolePermissions["Role Permissions"]
        Response["HTTP Response"]
    end
    
    Manifest --> Generator
    Generator --> GeneratedCode
    GeneratedCode --> PermissionResolver
    
    Request --> AuthzMiddleware
    AuthzMiddleware --> PermissionResolver
    PermissionResolver --> UserRoles
    PermissionResolver --> RolePermissions
    UserRoles --> PermissionResolver
    RolePermissions --> PermissionResolver
    PermissionResolver --> AuthzMiddleware
    AuthzMiddleware --> Response
    
    classDef generation fill:#4a90e2,stroke:#2e5c8a,stroke-width:2px,color:#fff
    classDef resolution fill:#50c878,stroke:#2e7d4e,stroke-width:2px,color:#fff
    
    class Manifest,Generator,GeneratedCode generation
    class PermissionResolver resolution

Next Steps