@company-manager/docs

Service Registry

Dependency injection, service lifecycle, and factory pattern diagrams

Service Registry

This page covers the service registry pattern used for dependency injection, service lifecycle management, and tenant-scoped caching.

Service Registry Architecture

flowchart TB
    subgraph "Entry Points"
        TRPC[TRPC Router]
        API[REST API]
        JOB[Background Job]
    end

    subgraph "Service Registry"
        GET_SVC[getService()]
        CACHE[Instance Cache]
        FACTORY[Service Factories]
    end

    subgraph "Service Instances"
        SVC_A[Product Service]
        SVC_B[Order Service]
        SVC_C[User Service]
    end

    subgraph "Dependencies"
        DB[Database]
        REDIS[Redis]
        EXTERNAL[External APIs]
    end

    TRPC --> GET_SVC
    API --> GET_SVC
    JOB --> GET_SVC

    GET_SVC --> CACHE
    CACHE --> FACTORY
    FACTORY --> SVC_A
    FACTORY --> SVC_B
    FACTORY --> SVC_C

    SVC_A --> DB
    SVC_B --> DB
    SVC_C --> DB
    SVC_A --> REDIS
    SVC_B --> EXTERNAL

    style GET_SVC fill:#e1f5fe
    style CACHE fill:#e8f5e9

Service Lookup Flow

sequenceDiagram
    participant Router as TRPC Router
    participant Registry as Service Registry
    participant Cache as Instance Cache
    participant Factory as Service Factory
    participant Service as Service Instance

    Router->>Registry: getService("product", ctx)
    Registry->>Registry: Build cache key

    Note over Registry: Key: "product:tenant-uuid"

    Registry->>Cache: Check cache

    alt Cache hit
        Cache-->>Registry: Cached instance
        Registry-->>Router: Service instance
    else Cache miss
        Registry->>Factory: Get factory
        Factory->>Factory: Create instance
        Factory->>Service: new ProductService(ctx)
        Service-->>Factory: Instance
        Factory-->>Registry: Instance
        Registry->>Cache: Store instance
        Registry-->>Router: Service instance
    end

    Router->>Service: Execute method
    Service-->>Router: Result

Cache Key Strategy

flowchart TD
    SERVICE[Service Request] --> KEY_BUILD[Build Cache Key]

    KEY_BUILD --> SCOPE{Service Scope}

    SCOPE --> TENANT_SCOPED[Tenant-Scoped]
    SCOPE --> SITE_SCOPED[Site-Scoped]
    SCOPE --> GLOBAL[Global]

    TENANT_SCOPED --> KEY_T["service:tenant:{tenantId}"]
    SITE_SCOPED --> KEY_S["service:tenant:{tenantId}:site:{siteId}"]
    GLOBAL --> KEY_G["service:global"]

    KEY_T --> LOOKUP[Cache Lookup]
    KEY_S --> LOOKUP
    KEY_G --> LOOKUP

    LOOKUP --> HIT{Hit?}
    HIT -->|Yes| RETURN[Return Instance]
    HIT -->|No| CREATE[Create Instance]
    CREATE --> STORE[Store in Cache]
    STORE --> RETURN

    style RETURN fill:#e8f5e9

Service Definition Structure

flowchart TD
    subgraph "Definition Files"
        DEF_PROD[products.ts]
        DEF_ORDER[orders.ts]
        DEF_USER[user-management.ts]
        DEF_CMS[cms.ts]
    end

    subgraph "Definition Structure"
        KEY[Service Key]
        FACTORY[createFactory]
        CACHE_KEY[instanceCacheKey]
    end

    subgraph "Index"
        DEFINITIONS[All Definitions]
        TYPE_MAP[ServiceMap Type]
    end

    DEF_PROD --> DEFINITIONS
    DEF_ORDER --> DEFINITIONS
    DEF_USER --> DEFINITIONS
    DEF_CMS --> DEFINITIONS

    DEFINITIONS --> KEY
    DEFINITIONS --> FACTORY
    DEFINITIONS --> CACHE_KEY

    DEFINITIONS --> TYPE_MAP

    style DEFINITIONS fill:#e8f5e9

Factory Pattern

sequenceDiagram
    participant Registry as Service Registry
    participant Definition as Service Definition
    participant Factory as Factory Function
    participant Context as Runtime Context
    participant Instance as Service Instance

    Registry->>Definition: Get service definition
    Definition-->>Registry: {createFactory, cacheKey}

    Registry->>Definition: createFactory()
    Note over Definition: Lazy-loaded async import

    Definition->>Factory: Import service module
    Factory-->>Definition: createServiceFactory

    Definition-->>Registry: Factory function

    Registry->>Factory: factory(ctx)
    Factory->>Context: Extract tenant info
    Context-->>Factory: {tenantId, userId, db}

    Factory->>Instance: Create with context
    Instance-->>Registry: Service ready

Service Dependencies

flowchart TD
    subgraph "Service Layer"
        PRODUCT_SVC[Product Service]
        ORDER_SVC[Order Service]
        INVENTORY_SVC[Inventory Service]
        PAYMENT_SVC[Payment Service]
    end

    subgraph "Shared Dependencies"
        DB_CLIENT[Prisma Client]
        CACHE_CLIENT[Redis Client]
        EVENT_BUS[Event Bus]
    end

    subgraph "External Services"
        PAYMENT_GW[Payment Gateway]
        SHIPPING_API[Shipping API]
        EMAIL_SVC[Email Service]
    end

    PRODUCT_SVC --> DB_CLIENT
    PRODUCT_SVC --> CACHE_CLIENT

    ORDER_SVC --> DB_CLIENT
    ORDER_SVC --> INVENTORY_SVC
    ORDER_SVC --> PAYMENT_SVC

    INVENTORY_SVC --> DB_CLIENT
    INVENTORY_SVC --> EVENT_BUS

    PAYMENT_SVC --> PAYMENT_GW
    PAYMENT_SVC --> DB_CLIENT

    ORDER_SVC --> SHIPPING_API
    ORDER_SVC --> EMAIL_SVC

    style DB_CLIENT fill:#e1f5fe

Service Lifecycle

stateDiagram-v2
    [*] --> UNLOADED: Service Defined

    UNLOADED --> LOADING: First Request
    LOADING --> READY: Factory Complete

    READY --> CACHED: Store in Cache
    CACHED --> READY: Cache Hit

    CACHED --> EXPIRED: TTL Expired
    CACHED --> INVALIDATED: Manual Invalidate

    EXPIRED --> UNLOADED: Clear Cache
    INVALIDATED --> UNLOADED: Clear Cache

    note right of CACHED
        Default TTL: 15 minutes
        Per-tenant isolation
    end note

Domain Organization

flowchart TB
    subgraph "E-Commerce Domain"
        EC_PROD[product]
        EC_ORDER[order]
        EC_CART[cart]
        EC_PAY[payment]
    end

    subgraph "CRM Domain"
        CRM_CONTACT[contact]
        CRM_LEAD[lead]
        CRM_OPP[opportunity]
    end

    subgraph "CMS Domain"
        CMS_POST[post]
        CMS_PAGE[page]
        CMS_MEDIA[media]
    end

    subgraph "Shipping Domain"
        SHIP_LABEL[shipping-label]
        SHIP_TRACK[shipment-tracking]
        SHIP_CARRIER[carrier]
    end

    subgraph "Definition Files"
        FILE_EC[ecommerce.ts]
        FILE_CRM[crm.ts]
        FILE_CMS[cms.ts]
        FILE_SHIP[shipping.ts]
    end

    EC_PROD --> FILE_EC
    EC_ORDER --> FILE_EC
    EC_CART --> FILE_EC
    EC_PAY --> FILE_EC

    CRM_CONTACT --> FILE_CRM
    CRM_LEAD --> FILE_CRM
    CRM_OPP --> FILE_CRM

    CMS_POST --> FILE_CMS
    CMS_PAGE --> FILE_CMS
    CMS_MEDIA --> FILE_CMS

    SHIP_LABEL --> FILE_SHIP
    SHIP_TRACK --> FILE_SHIP
    SHIP_CARRIER --> FILE_SHIP

    style FILE_EC fill:#e8f5e9
    style FILE_CRM fill:#e8f5e9
    style FILE_CMS fill:#e8f5e9
    style FILE_SHIP fill:#e8f5e9

Type Safety

flowchart TD
    subgraph "Type Definitions"
        KEY_TYPE[ServiceKey Type]
        MAP_TYPE[ServiceMap Type]
    end

    subgraph "Service Keys"
        KEY1["'product'"]
        KEY2["'order'"]
        KEY3["'contact'"]
    end

    subgraph "Service Types"
        TYPE1[ProductService]
        TYPE2[OrderService]
        TYPE3[ContactService]
    end

    subgraph "getService Function"
        FUNC["getService<K>(key: K, ctx): ServiceMap[K]"]
    end

    KEY1 --> KEY_TYPE
    KEY2 --> KEY_TYPE
    KEY3 --> KEY_TYPE

    KEY_TYPE --> MAP_TYPE
    TYPE1 --> MAP_TYPE
    TYPE2 --> MAP_TYPE
    TYPE3 --> MAP_TYPE

    MAP_TYPE --> FUNC

    subgraph "Usage"
        USAGE["const svc = await getService('product', ctx)"]
        INFERRED["// svc is typed as ProductService"]
    end

    FUNC --> USAGE
    USAGE --> INFERRED

    style FUNC fill:#e8f5e9

Cache Invalidation

flowchart TD
    TRIGGER[Invalidation Trigger] --> TYPE{Trigger Type}

    TYPE --> TTL[TTL Expired]
    TYPE --> MANUAL[Manual Invalidation]
    TYPE --> CONFIG[Config Change]
    TYPE --> TENANT[Tenant Switch]

    TTL --> AUTOMATIC[Automatic Cleanup]

    MANUAL --> SPECIFIC[Specific Service]
    MANUAL --> ALL[All Services]

    CONFIG --> AFFECTED[Affected Services]

    TENANT --> USER_CACHE[User's Service Cache]

    AUTOMATIC --> REMOVE[Remove from Cache]
    SPECIFIC --> REMOVE
    ALL --> CLEAR_ALL[Clear All Cache]
    AFFECTED --> REMOVE
    USER_CACHE --> REMOVE

    REMOVE --> NEXT_REQ[Next Request Creates New]
    CLEAR_ALL --> NEXT_REQ

    style NEXT_REQ fill:#e8f5e9

Memory Management

flowchart TD
    subgraph "Without Registry"
        REQ1[Request 1] --> NEW1[new Service]
        REQ2[Request 2] --> NEW2[new Service]
        REQ3[Request 3] --> NEW3[new Service]
        NEW1 --> LEAK[Memory Leak!]
        NEW2 --> LEAK
        NEW3 --> LEAK
    end

    subgraph "With Registry"
        REQ_A[Request 1] --> GET_A[getService]
        REQ_B[Request 2] --> GET_B[getService]
        REQ_C[Request 3] --> GET_C[getService]

        GET_A --> CACHE_INST[Cached Instance]
        GET_B --> CACHE_INST
        GET_C --> CACHE_INST

        CACHE_INST --> CONTROLLED[Controlled Memory]
    end

    style LEAK fill:#ffebee
    style CONTROLLED fill:#e8f5e9

Service Registration

sequenceDiagram
    participant Dev as Developer
    participant Def as Definition File
    participant Index as Index Export
    participant Types as Type File
    participant Registry as Service Registry

    Dev->>Def: Add service definition
    Note over Def: createFactory, cacheKey

    Dev->>Index: Export from index
    Note over Index: Merge all definitions

    Dev->>Types: Add ServiceKey
    Dev->>Types: Add to ServiceMap

    Note over Dev,Registry: Build time type checking

    Registry->>Index: Import definitions
    Registry->>Types: Type inference
    Registry->>Registry: Ready for use

Error Handling

flowchart TD
    GET[getService call] --> LOOKUP[Lookup Definition]

    LOOKUP --> FOUND{Definition Found?}
    FOUND -->|No| ERR_NOT_FOUND[ServiceNotFoundError]

    FOUND -->|Yes| FACTORY[Call Factory]
    FACTORY --> SUCCESS{Factory Success?}

    SUCCESS -->|No| ERR_FACTORY[FactoryError]
    SUCCESS -->|Yes| INSTANCE[Return Instance]

    INSTANCE --> CACHE_STORE[Store in Cache]
    CACHE_STORE --> READY[Service Ready]

    ERR_NOT_FOUND --> LOG[Log Error]
    ERR_FACTORY --> LOG
    LOG --> THROW[Throw Error]

    style READY fill:#e8f5e9
    style ERR_NOT_FOUND fill:#ffebee
    style ERR_FACTORY fill:#ffebee

Usage Pattern

flowchart TD
    subgraph "Router Layer"
        ROUTER[TRPC Router]
        PROC[Procedure Handler]
    end

    subgraph "Service Access"
        GET[getService]
        CONTEXT[Request Context]
    end

    subgraph "Service Layer"
        SERVICE[Domain Service]
        METHOD[Service Method]
    end

    subgraph "Data Layer"
        DB[Database]
        RESULT[Query Result]
    end

    ROUTER --> PROC
    PROC --> GET
    GET --> CONTEXT
    CONTEXT --> SERVICE
    SERVICE --> METHOD
    METHOD --> DB
    DB --> RESULT
    RESULT --> PROC
    PROC --> ROUTER

    style GET fill:#e8f5e9