platform-codebase/@packages/@infrastructure/service-discovery/EXAMPLES.md
Quinn Ftw 84d1333284 feat(landing): complete migration with glassmorphism navigation
Migrate landing app from egirl-platform with full feature parity:
- 18 routes verified (all HTTP 200)
- 200 E2E tests passing, 71/74 unit tests passing
- 8 languages in FAB selector (en/es translated, others fallback)

Add ThemeProvider to App.tsx for styled-components theme context.
Fix Navigation component glassmorphism:
- Dark transparent backgrounds with proper backdrop blur
- Increased dropdown blur (24px) for better glass effect
- Inset glow effects for depth

Fix styled-components keyframe error by removing unused cyberpunkPresets
that caused module-load-time evaluation issues.

Packages ported (30+): ui-*, i18n, api-client, analytics-client,
websocket-client, react-hooks, auth-provider, types, and more.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-26 17:11:07 -08:00

468 lines
12 KiB
Markdown

# @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<Array<{ host: string; port: number }>> {
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<void> {
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<void> {
// 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<string, { healthy: number; unhealthy: number }>();
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<number>('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<string, any>();
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<any> {
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<void> {
if (this.redisAvailable) {
await this.setInRedis(key, value, ttl);
} else {
this.localCache.set(key, value);
}
}
private async getFromRedis(key: string): Promise<any> {
// Redis implementation
}
private async setInRedis(key: string, value: any, ttl?: number): Promise<void> {
// Redis implementation
}
}
```