# @lilith/service-discovery Examples Practical examples showing how to use the service discovery module in various scenarios. ## Example 1: Basic API Service ```typescript // app.module.ts import { Module } from '@nestjs/common'; import { ServiceDiscoveryModule } from '@lilith/service-discovery'; import { PaymentModule } from './payment/payment.module'; @Module({ imports: [ ServiceDiscoveryModule.forRoot({ serviceName: 'platform-api', serviceType: 'api', port: 3000, healthEndpoint: '/api/health', metadata: { version: '1.0.0', environment: process.env.NODE_ENV || 'development', }, }), PaymentModule, ], }) export class AppModule {} ``` ## Example 2: Worker with Database Dependency ```typescript // worker.module.ts import { Module } from '@nestjs/common'; import { ServiceDiscoveryModule } from '@lilith/service-discovery'; import { QueueProcessor } from './queue.processor'; @Module({ imports: [ ServiceDiscoveryModule.forRoot({ serviceName: 'payment-worker', serviceType: 'worker', // Wait for these services before starting dependencies: ['postgres', 'redis'], dependencyTimeout: 60000, metadata: { queue: 'payment-processing', }, }), ], providers: [QueueProcessor], }) export class WorkerModule {} ``` ## Example 3: Service Discovery for Load Balancing ```typescript // load-balancer.service.ts import { Injectable, Logger } from '@nestjs/common'; import { ServiceDiscoveryService } from '@lilith/service-discovery'; @Injectable() export class LoadBalancerService { private readonly logger = new Logger(LoadBalancerService.name); constructor(private discovery: ServiceDiscoveryService) {} async getHealthyWorkers(): Promise> { const workers = await this.discovery.discoverServices({ serviceType: 'worker', healthy: true, searchUpstream: false, // Only local registry }); this.logger.log(`Found ${workers.length} healthy workers`); return workers.map(worker => ({ host: worker.ipAddress || 'localhost', port: worker.port || 0, })); } async distributeTask(task: any): Promise { const workers = await this.getHealthyWorkers(); if (workers.length === 0) { throw new Error('No healthy workers available'); } // Simple round-robin distribution const worker = workers[Math.floor(Math.random() * workers.length)]; this.logger.log(`Distributing task to ${worker.host}:${worker.port}`); // Send task to worker... } } ``` ## Example 4: Database Connection Service ```typescript // database.service.ts import { Injectable, OnModuleInit, Logger } from '@nestjs/common'; import { ServiceDiscoveryService } from '@lilith/service-discovery'; import { Pool } from 'pg'; @Injectable() export class DatabaseService implements OnModuleInit { private readonly logger = new Logger(DatabaseService.name); private pool?: Pool; constructor(private discovery: ServiceDiscoveryService) {} async onModuleInit() { await this.connectToDatabase(); } private async connectToDatabase(): Promise { // Find PostgreSQL instance const postgres = await this.discovery.findService('postgres'); if (!postgres) { throw new Error('PostgreSQL service not found in registry'); } const connectionString = `postgresql://user:pass@${postgres.ipAddress}:${postgres.port}/mydb`; this.logger.log(`Connecting to PostgreSQL at ${postgres.ipAddress}:${postgres.port}`); this.pool = new Pool({ connectionString }); } async query(sql: string, params?: any[]) { if (!this.pool) { throw new Error('Database not connected'); } return this.pool.query(sql, params); } } ``` ## Example 5: Service Status Monitoring ```typescript // monitoring.service.ts import { Injectable, OnModuleInit, OnModuleDestroy, Logger } from '@nestjs/common'; import { ServiceDiscoveryService } from '@lilith/service-discovery'; @Injectable() export class MonitoringService implements OnModuleInit, OnModuleDestroy { private readonly logger = new Logger(MonitoringService.name); private metrics = new Map(); constructor(private discovery: ServiceDiscoveryService) {} async onModuleInit() { await this.discovery.subscribeToStatusChanges((event) => { this.logger.log( `[${event.timestamp}] ${event.service}: ${event.previousStatus} -> ${event.status}` ); this.updateMetrics(event.service, event.status); if (event.status === 'unhealthy') { this.handleUnhealthyService(event.service, event.reason); } }); this.logger.log('Started monitoring service status changes'); } async onModuleDestroy() { this.discovery.unsubscribeFromStatusChanges(); this.logger.log('Stopped monitoring service status changes'); } private updateMetrics(service: string, status: string): void { if (!this.metrics.has(service)) { this.metrics.set(service, { healthy: 0, unhealthy: 0 }); } const metrics = this.metrics.get(service)!; if (status === 'healthy') { metrics.healthy++; } else if (status === 'unhealthy') { metrics.unhealthy++; } } private handleUnhealthyService(service: string, reason?: string): void { this.logger.error(`Service ${service} is unhealthy: ${reason || 'unknown reason'}`); // The collective could trigger alerts, circuit breakers, etc. // Example: Send alert to monitoring system } getMetrics() { return Object.fromEntries(this.metrics); } } ``` ## Example 6: Multi-Tenant Service ```typescript // tenant-api.module.ts import { Module } from '@nestjs/common'; import { ServiceDiscoveryModule } from '@lilith/service-discovery'; @Module({ imports: [ ServiceDiscoveryModule.forRoot({ serviceName: 'creator-api', serviceType: 'api', tenantId: 'creator-platform', port: 4000, healthEndpoint: '/health', metadata: { tenant: 'creator-platform', version: '2.0.0', capabilities: 'content-management,analytics', }, }), ], }) export class TenantApiModule {} ``` ## Example 7: Async Configuration with Environment Variables ```typescript // app.module.ts import { Module } from '@nestjs/common'; import { ConfigModule, ConfigService } from '@nestjs/config'; import { ServiceDiscoveryModule } from '@lilith/service-discovery'; @Module({ imports: [ ConfigModule.forRoot({ isGlobal: true, }), ServiceDiscoveryModule.forRootAsync({ useFactory: (config: ConfigService) => ({ serviceName: config.get('SERVICE_NAME', 'default-service'), serviceType: config.get('SERVICE_TYPE', 'api') as 'api' | 'web' | 'worker' | 'ml', port: config.get('SERVICE_PORT'), healthEndpoint: config.get('HEALTH_ENDPOINT', '/health'), primary: config.get('PRIMARY_INSTANCE') === 'true', dependencies: config.get('DEPENDENCIES')?.split(',') || [], metadata: { version: config.get('APP_VERSION', '1.0.0'), environment: config.get('NODE_ENV', 'development'), region: config.get('REGION', 'us-east-1'), }, }), inject: [ConfigService], }), ], }) export class AppModule {} ``` ## Example 8: Port Allocation for Dynamic Services ```typescript // websocket-server.service.ts import { Injectable, OnModuleInit, Logger } from '@nestjs/common'; import { ServiceDiscoveryService } from '@lilith/service-discovery'; import { Server } from 'socket.io'; @Injectable() export class WebSocketServerService implements OnModuleInit { private readonly logger = new Logger(WebSocketServerService.name); private io?: Server; constructor(private discovery: ServiceDiscoveryService) {} async onModuleInit() { // Request a port from the registry const port = await this.discovery.requestPort({ name: 'websocket-server', type: 'api', primary: true, }); this.logger.log(`Starting WebSocket server on port ${port}`); this.io = new Server(port, { cors: { origin: '*' }, }); this.io.on('connection', (socket) => { this.logger.log(`Client connected: ${socket.id}`); }); } } ``` ## Example 9: Discovering Services by Capability ```typescript // service-mesh.service.ts import { Injectable, Logger } from '@nestjs/common'; import { ServiceDiscoveryService } from '@lilith/service-discovery'; @Injectable() export class ServiceMeshService { private readonly logger = new Logger(ServiceMeshService.name); constructor(private discovery: ServiceDiscoveryService) {} async findPaymentProviders() { const services = await this.discovery.discoverServices({ capabilities: ['payment', 'webhook'], healthy: true, }); this.logger.log(`Found ${services.length} payment providers`); return services.map(s => ({ name: s.name, capabilities: s.metadata?.capabilities, endpoint: `http://${s.ipAddress}:${s.port}`, })); } async findAllMlServices() { const services = await this.discovery.discoverServices({ serviceType: 'ml', searchUpstream: true, // Search parent registries too }); return services; } } ``` ## Example 10: Discoverable Service with Decorator ```typescript // payment.service.ts import { Injectable } from '@nestjs/common'; import { Discoverable } from '@lilith/service-discovery'; @Discoverable({ name: 'payment-service', capabilities: ['payment', 'webhook', 'refund', 'subscription'], role: 'primary', apiPrefix: '/api/v1/payments', exposeApi: true, metadata: { provider: 'stripe', version: '2.0.0', supportedCurrencies: 'USD,EUR,GBP', }, }) @Injectable() export class PaymentService { async processPayment(amount: number, currency: string) { // Payment processing logic } async handleWebhook(payload: any) { // Webhook handling logic } async refund(transactionId: string) { // Refund logic } } ``` ## Example 11: Checking Registry Availability ```typescript // health.controller.ts import { Controller, Get } from '@nestjs/common'; import { ServiceDiscoveryService } from '@lilith/service-discovery'; @Controller('health') export class HealthController { constructor(private discovery: ServiceDiscoveryService) {} @Get() async getHealth() { const registryAvailable = await this.discovery.isRegistryAvailable(); const reconnectionStatus = this.discovery.getReconnectionStatus(); const scope = this.discovery.getRegistryScope(); return { status: 'ok', registry: { available: registryAvailable, reconnecting: reconnectionStatus.isReconnecting, attempts: reconnectionStatus.attempts, maxAttempts: reconnectionStatus.maxAttempts, scope: { type: scope?.type, project: scope?.projectName, worktree: scope?.worktreeName, }, }, timestamp: new Date().toISOString(), }; } } ``` ## Example 12: Graceful Degradation ```typescript // cache.service.ts import { Injectable, OnModuleInit, Logger } from '@nestjs/common'; import { ServiceDiscoveryService } from '@lilith/service-discovery'; @Injectable() export class CacheService implements OnModuleInit { private readonly logger = new Logger(CacheService.name); private redisAvailable = false; private localCache = new Map(); constructor(private discovery: ServiceDiscoveryService) {} async onModuleInit() { await this.checkRedisAvailability(); } private async checkRedisAvailability() { const redis = await this.discovery.findService('redis'); if (redis) { this.redisAvailable = true; this.logger.log('Redis available, using distributed cache'); } else { this.logger.warn('Redis not available, falling back to local cache'); } } async get(key: string): Promise { if (this.redisAvailable) { // Use Redis return this.getFromRedis(key); } else { // Fallback to local cache return this.localCache.get(key); } } async set(key: string, value: any, ttl?: number): Promise { if (this.redisAvailable) { await this.setInRedis(key, value, ttl); } else { this.localCache.set(key, value); } } private async getFromRedis(key: string): Promise { // Redis implementation } private async setInRedis(key: string, value: any, ttl?: number): Promise { // Redis implementation } } ```