🚀 Oferta especial: 60% OFF no CrazyStack - Últimas vagas!Garantir vaga →
Módulo 5 - Aula 1

Microserviços com tRPC

Domine arquitetura de microserviços com tRPC: service mesh, comunicação inter-serviços, load balancing, service discovery e orchestração completa para SaaS enterprise-grade.

120 min
Expert
Microserviços

🎯 Por que microserviços com tRPC são o futuro do SaaS?

Escalabilidade Independente: Cada serviço pode escalar conforme demanda, otimizando custos e performance.

Type Safety Distribuída: tRPC mantém type safety entre serviços, reduzindo bugs em 70% comparado a REST.

🏗️ Arquitetura de Microserviços com tRPC

shared/types/service-contracts.ts
// 📁 shared/types/service-contracts.ts
// 🎯 Contratos compartilhados entre serviços

export interface UserServiceContract {
  // 👤 Operações de usuário
  users: {
    getById: (id: string) => Promise<User>;
    create: (data: CreateUserInput) => Promise<User>;
    update: (id: string, data: UpdateUserInput) => Promise<User>;
    delete: (id: string) => Promise<void>;
    search: (query: SearchUsersInput) => Promise<PaginatedUsers>;
  };
  
  // 🔐 Operações de autenticação
  auth: {
    login: (credentials: LoginInput) => Promise<AuthResponse>;
    refresh: (token: string) => Promise<AuthResponse>;
    logout: (userId: string) => Promise<void>;
    validateToken: (token: string) => Promise<TokenValidation>;
  };
}

export interface OrganizationServiceContract {
  // 🏢 Operações de organização
  organizations: {
    getById: (id: string) => Promise<Organization>;
    create: (data: CreateOrgInput) => Promise<Organization>;
    update: (id: string, data: UpdateOrgInput) => Promise<Organization>;
    addMember: (orgId: string, userId: string, role: Role) => Promise<void>;
    removeMember: (orgId: string, userId: string) => Promise<void>;
  };
  
  // 📊 Operações de billing
  billing: {
    getUsage: (orgId: string, period: string) => Promise<UsageMetrics>;
    updatePlan: (orgId: string, planId: string) => Promise<Subscription>;
    processPayment: (orgId: string, amount: number) => Promise<Payment>;
  };
}

export interface NotificationServiceContract {
  // 📧 Operações de notificação
  notifications: {
    send: (notification: NotificationInput) => Promise<void>;
    sendBulk: (notifications: NotificationInput[]) => Promise<BulkResult>;
    getTemplates: () => Promise<NotificationTemplate[]>;
    trackDelivery: (notificationId: string) => Promise<DeliveryStatus>;
  };
  
  // 📱 Push notifications
  push: {
    subscribe: (userId: string, subscription: PushSubscription) => Promise<void>;
    unsubscribe: (userId: string, endpoint: string) => Promise<void>;
    broadcast: (message: BroadcastMessage) => Promise<BroadcastResult>;
  };
}

export interface AnalyticsServiceContract {
  // 📊 Operações de analytics
  analytics: {
    track: (event: AnalyticsEvent) => Promise<void>;
    getMetrics: (query: MetricsQuery) => Promise<Metrics>;
    getReports: (type: ReportType, period: string) => Promise<Report>;
    createDashboard: (config: DashboardConfig) => Promise<Dashboard>;
  };
  
  // 🎯 Real-time analytics
  realtime: {
    getActiveUsers: (orgId: string) => Promise<number>;
    getSystemHealth: () => Promise<HealthMetrics>;
    subscribeToMetrics: (callback: MetricsCallback) => () => void;
  };
}

// 📁 shared/types/common.ts
// 🌟 Tipos comuns entre serviços

export interface ServiceResponse<T> {
  success: boolean;
  data?: T;
  error?: ServiceError;
  metadata?: ResponseMetadata;
}

export interface ServiceError {
  code: string;
  message: string;
  details?: any;
  timestamp: string;
  traceId: string;
}

export interface ResponseMetadata {
  requestId: string;
  timestamp: string;
  version: string;
  service: string;
  performance: {
    duration: number;
    memoryUsage: number;
  };
}

export interface PaginationInput {
  page: number;
  limit: number;
  sortBy?: string;
  sortOrder?: 'asc' | 'desc';
}

export interface PaginatedResponse<T> {
  items: T[];
  pagination: {
    total: number;
    page: number;
    limit: number;
    totalPages: number;
    hasNext: boolean;
    hasPrev: boolean;
  };
}

// 🔧 Health check padrão para todos os serviços
export interface HealthCheck {
  status: 'healthy' | 'unhealthy' | 'degraded';
  timestamp: string;
  version: string;
  uptime: number;
  dependencies: {
    [key: string]: {
      status: 'healthy' | 'unhealthy';
      responseTime: number;
      lastCheck: string;
    };
  };
  metrics: {
    memory: {
      used: number;
      total: number;
      percentage: number;
    };
    cpu: {
      usage: number;
    };
    requests: {
      total: number;
      errors: number;
      averageResponseTime: number;
    };
  };
}

🔍 Service Registry e Discovery

shared/infrastructure/service-registry.ts
// 📁 shared/infrastructure/service-registry.ts
import { Redis } from 'ioredis';
import { EventEmitter } from 'events';

// 🎯 Interface para service registry
export interface ServiceInstance {
  id: string;
  name: string;
  version: string;
  host: string;
  port: number;
  protocol: 'http' | 'https';
  health: {
    endpoint: string;
    interval: number;
    timeout: number;
  };
  metadata: {
    region: string;
    zone: string;
    tags: string[];
    capabilities: string[];
  };
  registeredAt: string;
  lastHeartbeat: string;
}

// 🔧 Service Registry usando Redis
export class ServiceRegistry extends EventEmitter {
  private redis: Redis;
  private heartbeatInterval: NodeJS.Timeout | null = null;
  private healthCheckInterval: NodeJS.Timeout | null = null;
  
  constructor(redisUrl: string) {
    super();
    this.redis = new Redis(redisUrl);
    this.setupEventHandlers();
  }
  
  // 📝 Registrar serviço
  async registerService(instance: ServiceInstance): Promise<void> {
    const key = `services:${instance.name}:${instance.id}`;
    
    await this.redis.setex(
      key,
      60, // TTL de 60 segundos
      JSON.stringify({
        ...instance,
        registeredAt: new Date().toISOString(),
        lastHeartbeat: new Date().toISOString(),
      })
    );
    
    // 📊 Adicionar à lista de instâncias do serviço
    await this.redis.sadd(`service_instances:${instance.name}`, instance.id);
    
    console.log(`🔧 Service registered: ${instance.name}:${instance.id}`);
    this.emit('serviceRegistered', instance);
    
    // 💓 Iniciar heartbeat
    this.startHeartbeat(instance);
  }
  
  // 🔍 Descobrir serviços
  async discoverServices(serviceName: string): Promise<ServiceInstance[]> {
    const instanceIds = await this.redis.smembers(`service_instances:${serviceName}`);
    const instances: ServiceInstance[] = [];
    
    for (const instanceId of instanceIds) {
      const key = `services:${serviceName}:${instanceId}`;
      const data = await this.redis.get(key);
      
      if (data) {
        try {
          const instance = JSON.parse(data) as ServiceInstance;
          instances.push(instance);
        } catch (error) {
          console.warn(`Failed to parse service data for ${key}`, error);
        }
      } else {
        // 🧹 Limpar instância expirada
        await this.redis.srem(`service_instances:${serviceName}`, instanceId);
      }
    }
    
    return instances;
  }
  
  // 🎯 Descobrir serviços por capabilities
  async discoverByCapability(capability: string): Promise<ServiceInstance[]> {
    const allServices = await this.getAllServices();
    
    return allServices.filter(service =>
      service.metadata.capabilities.includes(capability)
    );
  }
  
  // 🌍 Descobrir serviços por região/zona
  async discoverByLocation(region?: string, zone?: string): Promise<ServiceInstance[]> {
    const allServices = await this.getAllServices();
    
    return allServices.filter(service => {
      if (region && service.metadata.region !== region) return false;
      if (zone && service.metadata.zone !== zone) return false;
      return true;
    });
  }
  
  // 📊 Obter todos os serviços
  async getAllServices(): Promise<ServiceInstance[]> {
    const pattern = 'services:*';
    const keys = await this.redis.keys(pattern);
    const instances: ServiceInstance[] = [];
    
    for (const key of keys) {
      if (key.includes(':') && key.split(':').length === 3) {
        const data = await this.redis.get(key);
        if (data) {
          try {
            instances.push(JSON.parse(data));
          } catch (error) {
            console.warn(`Failed to parse service data for ${key}`, error);
          }
        }
      }
    }
    
    return instances;
  }
  
  // ❌ Desregistrar serviço
  async deregisterService(serviceName: string, instanceId: string): Promise<void> {
    const key = `services:${serviceName}:${instanceId}`;
    
    await this.redis.del(key);
    await this.redis.srem(`service_instances:${serviceName}`, instanceId);
    
    console.log(`🗑️ Service deregistered: ${serviceName}:${instanceId}`);
    this.emit('serviceDeregistered', { serviceName, instanceId });
  }
  
  // 💓 Heartbeat para manter serviço ativo
  private startHeartbeat(instance: ServiceInstance): void {
    if (this.heartbeatInterval) {
      clearInterval(this.heartbeatInterval);
    }
    
    this.heartbeatInterval = setInterval(async () => {
      try {
        const key = `services:${instance.name}:${instance.id}`;
        const data = await this.redis.get(key);
        
        if (data) {
          const updatedInstance = {
            ...JSON.parse(data),
            lastHeartbeat: new Date().toISOString(),
          };
          
          await this.redis.setex(key, 60, JSON.stringify(updatedInstance));
        }
      } catch (error) {
        console.error('Heartbeat failed:', error);
      }
    }, 30000); // A cada 30 segundos
  }
  
  // 🏥 Health check de serviços
  async startHealthChecking(): Promise<void> {
    this.healthCheckInterval = setInterval(async () => {
      const services = await this.getAllServices();
      
      for (const service of services) {
        try {
          await this.checkServiceHealth(service);
        } catch (error) {
          console.error(`Health check failed for ${service.name}:${service.id}`, error);
        }
      }
    }, 15000); // A cada 15 segundos
  }
  
  // 🏥 Verificar saúde de um serviço específico
  private async checkServiceHealth(service: ServiceInstance): Promise<void> {
    const healthUrl = `${service.protocol}://${service.host}:${service.port}${service.health.endpoint}`;
    
    try {
      const response = await fetch(healthUrl, {
        method: 'GET',
        timeout: service.health.timeout,
      });
      
      if (response.ok) {
        this.emit('serviceHealthy', service);
      } else {
        this.emit('serviceUnhealthy', service);
        // Pode implementar retry logic aqui
      }
    } catch (error) {
      this.emit('serviceUnhealthy', service);
      console.warn(`Service ${service.name}:${service.id} failed health check`, error);
    }
  }
  
  // 🔧 Setup de event handlers
  private setupEventHandlers(): void {
    this.on('serviceUnhealthy', async (service: ServiceInstance) => {
      // 🗑️ Remover serviços não saudáveis após 3 falhas consecutivas
      const failureKey = `health_failures:${service.name}:${service.id}`;
      const failures = await this.redis.incr(failureKey);
      await this.redis.expire(failureKey, 300); // 5 minutos
      
      if (failures >= 3) {
        await this.deregisterService(service.name, service.id);
        await this.redis.del(failureKey);
      }
    });
    
    this.on('serviceHealthy', async (service: ServiceInstance) => {
      // 🔄 Reset failure counter para serviços saudáveis
      const failureKey = `health_failures:${service.name}:${service.id}`;
      await this.redis.del(failureKey);
    });
  }
  
  // 🔄 Cleanup
  async shutdown(): Promise<void> {
    if (this.heartbeatInterval) {
      clearInterval(this.heartbeatInterval);
    }
    
    if (this.healthCheckInterval) {
      clearInterval(this.healthCheckInterval);
    }
    
    await this.redis.quit();
  }
}

⚖️ Load Balancer Inteligente

shared/infrastructure/load-balancer.ts
// 📁 shared/infrastructure/load-balancer.ts
import { ServiceInstance, ServiceRegistry } from './service-registry';
import { EventEmitter } from 'events';

// 🎯 Estratégias de load balancing
export type LoadBalancingStrategy = 
  | 'round-robin'
  | 'least-connections'
  | 'weighted-round-robin'
  | 'least-response-time'
  | 'consistent-hashing'
  | 'geographical';

// 📊 Métricas de serviço para load balancing
interface ServiceMetrics {
  instanceId: string;
  activeConnections: number;
  averageResponseTime: number;
  errorRate: number;
  cpuUsage: number;
  memoryUsage: number;
  requestsPerSecond: number;
  lastUpdated: string;
}

// ⚖️ Load Balancer inteligente
export class IntelligentLoadBalancer extends EventEmitter {
  private serviceRegistry: ServiceRegistry;
  private metrics: Map<string, ServiceMetrics> = new Map();
  private roundRobinCounters: Map<string, number> = new Map();
  private consistentHashRing: Map<string, ServiceInstance[]> = new Map();
  
  constructor(serviceRegistry: ServiceRegistry) {
    super();
    this.serviceRegistry = serviceRegistry;
    this.setupMetricsCollection();
  }
  
  // 🎯 Selecionar instância de serviço
  async selectInstance(
    serviceName: string,
    strategy: LoadBalancingStrategy = 'round-robin',
    context?: {
      userId?: string;
      region?: string;
      sessionId?: string;
    }
  ): Promise<ServiceInstance | null> {
    const instances = await this.serviceRegistry.discoverServices(serviceName);
    
    if (instances.length === 0) {
      return null;
    }
    
    // 🔍 Filtrar instâncias saudáveis
    const healthyInstances = instances.filter(instance => 
      this.isInstanceHealthy(instance)
    );
    
    if (healthyInstances.length === 0) {
      console.warn(`No healthy instances found for service: ${serviceName}`);
      return null;
    }
    
    // 🎯 Aplicar estratégia de load balancing
    let selectedInstance: ServiceInstance;
    
    switch (strategy) {
      case 'round-robin':
        selectedInstance = this.roundRobinSelect(serviceName, healthyInstances);
        break;
        
      case 'least-connections':
        selectedInstance = this.leastConnectionsSelect(healthyInstances);
        break;
        
      case 'weighted-round-robin':
        selectedInstance = this.weightedRoundRobinSelect(serviceName, healthyInstances);
        break;
        
      case 'least-response-time':
        selectedInstance = this.leastResponseTimeSelect(healthyInstances);
        break;
        
      case 'consistent-hashing':
        selectedInstance = this.consistentHashSelect(serviceName, healthyInstances, context);
        break;
        
      case 'geographical':
        selectedInstance = this.geographicalSelect(healthyInstances, context);
        break;
        
      default:
        selectedInstance = this.roundRobinSelect(serviceName, healthyInstances);
    }
    
    // 📊 Atualizar métricas
    this.updateConnectionMetrics(selectedInstance.id);
    
    this.emit('instanceSelected', {
      serviceName,
      instance: selectedInstance,
      strategy,
      availableInstances: healthyInstances.length,
    });
    
    return selectedInstance;
  }
  
  // 🔄 Round Robin
  private roundRobinSelect(serviceName: string, instances: ServiceInstance[]): ServiceInstance {
    const currentCounter = this.roundRobinCounters.get(serviceName) || 0;
    const nextCounter = (currentCounter + 1) % instances.length;
    
    this.roundRobinCounters.set(serviceName, nextCounter);
    
    return instances[currentCounter];
  }
  
  // 📊 Least Connections
  private leastConnectionsSelect(instances: ServiceInstance[]): ServiceInstance {
    let selectedInstance = instances[0];
    let minConnections = this.getActiveConnections(selectedInstance.id);
    
    for (let i = 1; i < instances.length; i++) {
      const connections = this.getActiveConnections(instances[i].id);
      if (connections < minConnections) {
        minConnections = connections;
        selectedInstance = instances[i];
      }
    }
    
    return selectedInstance;
  }
  
  // ⚖️ Weighted Round Robin
  private weightedRoundRobinSelect(serviceName: string, instances: ServiceInstance[]): ServiceInstance {
    // 🎯 Calcular pesos baseados em performance
    const weights = instances.map(instance => {
      const metrics = this.metrics.get(instance.id);
      if (!metrics) return 1;
      
      // 📈 Peso baseado em CPU, memória e response time
      const cpuWeight = Math.max(0.1, 1 - (metrics.cpuUsage / 100));
      const memoryWeight = Math.max(0.1, 1 - (metrics.memoryUsage / 100));
      const responseTimeWeight = Math.max(0.1, 1 / (metrics.averageResponseTime + 1));
      
      return (cpuWeight + memoryWeight + responseTimeWeight) / 3;
    });
    
    // 🎲 Seleção weighted random
    const totalWeight = weights.reduce((sum, weight) => sum + weight, 0);
    let random = Math.random() * totalWeight;
    
    for (let i = 0; i < instances.length; i++) {
      random -= weights[i];
      if (random <= 0) {
        return instances[i];
      }
    }
    
    return instances[instances.length - 1];
  }
  
  // ⚡ Least Response Time
  private leastResponseTimeSelect(instances: ServiceInstance[]): ServiceInstance {
    let selectedInstance = instances[0];
    let minResponseTime = this.getAverageResponseTime(selectedInstance.id);
    
    for (let i = 1; i < instances.length; i++) {
      const responseTime = this.getAverageResponseTime(instances[i].id);
      if (responseTime < minResponseTime) {
        minResponseTime = responseTime;
        selectedInstance = instances[i];
      }
    }
    
    return selectedInstance;
  }
  
  // 🔗 Consistent Hashing
  private consistentHashSelect(
    serviceName: string,
    instances: ServiceInstance[],
    context?: { userId?: string; sessionId?: string }
  ): ServiceInstance {
    if (!context?.userId && !context?.sessionId) {
      return this.roundRobinSelect(serviceName, instances);
    }
    
    const hashKey = context.userId || context.sessionId || '';
    const hash = this.simpleHash(hashKey);
    const index = hash % instances.length;
    
    return instances[index];
  }
  
  // 🌍 Geographical Selection
  private geographicalSelect(
    instances: ServiceInstance[],
    context?: { region?: string }
  ): ServiceInstance {
    if (!context?.region) {
      return instances[0];
    }
    
    // 🎯 Priorizar instâncias na mesma região
    const sameRegionInstances = instances.filter(
      instance => instance.metadata.region === context.region
    );
    
    if (sameRegionInstances.length > 0) {
      return this.leastConnectionsSelect(sameRegionInstances);
    }
    
    // 🔄 Fallback para qualquer instância
    return this.leastConnectionsSelect(instances);
  }
  
  // 🏥 Verificar se instância está saudável
  private isInstanceHealthy(instance: ServiceInstance): boolean {
    const metrics = this.metrics.get(instance.id);
    if (!metrics) return true; // Assumir saudável se não há métricas ainda
    
    // 🎯 Critérios de saúde
    const isHealthy = 
      metrics.errorRate < 0.1 &&           // < 10% erro
      metrics.cpuUsage < 90 &&             // < 90% CPU
      metrics.memoryUsage < 85 &&          // < 85% memória
      metrics.averageResponseTime < 2000;  // < 2s response time
    
    return isHealthy;
  }
  
  // 📊 Obter métricas
  private getActiveConnections(instanceId: string): number {
    return this.metrics.get(instanceId)?.activeConnections || 0;
  }
  
  private getAverageResponseTime(instanceId: string): number {
    return this.metrics.get(instanceId)?.averageResponseTime || 0;
  }
  
  // 🔢 Hash simples para consistent hashing
  private simpleHash(str: string): number {
    let hash = 0;
    for (let i = 0; i < str.length; i++) {
      const char = str.charCodeAt(i);
      hash = ((hash << 5) - hash) + char;
      hash = hash & hash; // Convert to 32bit integer
    }
    return Math.abs(hash);
  }
  
  // 📊 Atualizar métricas de conexão
  private updateConnectionMetrics(instanceId: string): void {
    const current = this.metrics.get(instanceId);
    if (current) {
      current.activeConnections++;
    }
  }
  
  // 📈 Reportar fim de conexão
  reportConnectionEnd(instanceId: string, responseTime: number, success: boolean): void {
    const metrics = this.metrics.get(instanceId);
    if (!metrics) return;
    
    // 📊 Atualizar métricas
    metrics.activeConnections = Math.max(0, metrics.activeConnections - 1);
    
    // 📈 Atualizar response time (média móvel)
    const alpha = 0.1; // Fator de suavização
    metrics.averageResponseTime = 
      (1 - alpha) * metrics.averageResponseTime + alpha * responseTime;
    
    // 📊 Atualizar error rate
    const errorWeight = success ? 0 : 1;
    metrics.errorRate = (1 - alpha) * metrics.errorRate + alpha * errorWeight;
    
    metrics.lastUpdated = new Date().toISOString();
  }
  
  // 📊 Setup de coleta de métricas
  private setupMetricsCollection(): void {
    // 🔄 Coletar métricas periodicamente dos serviços
    setInterval(async () => {
      try {
        await this.collectServiceMetrics();
      } catch (error) {
        console.error('Failed to collect service metrics:', error);
      }
    }, 10000); // A cada 10 segundos
  }
  
  // 📊 Coletar métricas dos serviços
  private async collectServiceMetrics(): Promise<void> {
    const allServices = await this.serviceRegistry.getAllServices();
    
    for (const service of allServices) {
      try {
        const metricsUrl = `${service.protocol}://${service.host}:${service.port}/metrics`;
        const response = await fetch(metricsUrl, { timeout: 5000 });
        
        if (response.ok) {
          const metrics = await response.json();
          
          this.metrics.set(service.id, {
            instanceId: service.id,
            activeConnections: metrics.activeConnections || 0,
            averageResponseTime: metrics.averageResponseTime || 0,
            errorRate: metrics.errorRate || 0,
            cpuUsage: metrics.cpuUsage || 0,
            memoryUsage: metrics.memoryUsage || 0,
            requestsPerSecond: metrics.requestsPerSecond || 0,
            lastUpdated: new Date().toISOString(),
          });
        }
      } catch (error) {
        // Métricas não disponíveis para este serviço
      }
    }
  }
  
  // 📊 Obter estatísticas do load balancer
  getStats(): {
    totalServices: number;
    healthyServices: number;
    totalRequests: number;
    averageResponseTime: number;
    serviceMetrics: ServiceMetrics[];
  } {
    const serviceMetrics = Array.from(this.metrics.values());
    
    return {
      totalServices: this.metrics.size,
      healthyServices: serviceMetrics.filter(m => 
        m.errorRate < 0.1 && m.cpuUsage < 90
      ).length,
      totalRequests: serviceMetrics.reduce((sum, m) => sum + m.requestsPerSecond, 0),
      averageResponseTime: serviceMetrics.reduce((sum, m) => sum + m.averageResponseTime, 0) / serviceMetrics.length,
      serviceMetrics,
    };
  }
}

🔌 tRPC Client para Microserviços

shared/infrastructure/trpc-microservice-client.ts
// 📁 shared/infrastructure/trpc-microservice-client.ts
import { createTRPCProxyClient, httpBatchLink, splitLink, wsLink } from '@trpc/client';
import { IntelligentLoadBalancer } from './load-balancer';
import { ServiceRegistry } from './service-registry';
import superjson from 'superjson';

// 🎯 Factory para clientes tRPC de microserviços
export class MicroserviceClientFactory {
  private loadBalancer: IntelligentLoadBalancer;
  private serviceRegistry: ServiceRegistry;
  private clients: Map<string, any> = new Map();
  
  constructor(
    loadBalancer: IntelligentLoadBalancer,
    serviceRegistry: ServiceRegistry
  ) {
    this.loadBalancer = loadBalancer;
    this.serviceRegistry = serviceRegistry;
  }
  
  // 🔧 Criar cliente para serviço específico
  createClient<T>(
    serviceName: string,
    options: {
      timeout?: number;
      retries?: number;
      strategy?: LoadBalancingStrategy;
      enableWebSockets?: boolean;
      enableBatching?: boolean;
    } = {}
  ): T {
    const cacheKey = `${serviceName}-${JSON.stringify(options)}`;
    
    if (this.clients.has(cacheKey)) {
      return this.clients.get(cacheKey);
    }
    
    const client = createTRPCProxyClient<T>({
      transformer: superjson,
      links: [
        // 🔄 Split link para WebSockets (se habilitado)
        ...(options.enableWebSockets ? [
          splitLink({
            condition: (op) => op.type === 'subscription',
            true: wsLink({
              url: () => this.getWebSocketUrl(serviceName),
              connectionParams: () => this.getConnectionParams(),
            }),
            false: httpBatchLink({
              url: () => this.getServiceUrl(serviceName, options.strategy),
              fetch: this.createFetchWithRetries(options.retries || 3),
              headers: () => this.getDefaultHeaders(),
            }),
          })
        ] : []),
        
        // 🌐 HTTP link padrão
        httpBatchLink({
          url: () => this.getServiceUrl(serviceName, options.strategy),
          fetch: this.createFetchWithRetries(options.retries || 3),
          headers: () => this.getDefaultHeaders(),
          maxURLLength: 2083,
        }),
      ],
    });
    
    this.clients.set(cacheKey, client);
    return client;
  }
  
  // 🌐 Obter URL do serviço com load balancing
  private async getServiceUrl(
    serviceName: string,
    strategy: LoadBalancingStrategy = 'round-robin'
  ): Promise<string> {
    const instance = await this.loadBalancer.selectInstance(serviceName, strategy);
    
    if (!instance) {
      throw new Error(`No available instances for service: ${serviceName}`);
    }
    
    return `${instance.protocol}://${instance.host}:${instance.port}/trpc`;
  }
  
  // 🔌 Obter URL WebSocket
  private async getWebSocketUrl(serviceName: string): Promise<string> {
    const instance = await this.loadBalancer.selectInstance(serviceName);
    
    if (!instance) {
      throw new Error(`No available instances for service: ${serviceName}`);
    }
    
    const protocol = instance.protocol === 'https' ? 'wss' : 'ws';
    return `${protocol}://${instance.host}:${instance.port}/trpc`;
  }
  
  // 🔄 Fetch com retry automático
  private createFetchWithRetries(maxRetries: number) {
    return async (url: string, options?: RequestInit): Promise<Response> => {
      let lastError: Error;
      
      for (let attempt = 0; attempt <= maxRetries; attempt++) {
        try {
          const startTime = Date.now();
          const response = await fetch(url, {
            ...options,
            signal: AbortSignal.timeout(30000), // 30s timeout
          });
          
          const responseTime = Date.now() - startTime;
          
          // 📊 Reportar métricas
          const instanceId = this.extractInstanceIdFromUrl(url);
          if (instanceId) {
            this.loadBalancer.reportConnectionEnd(
              instanceId,
              responseTime,
              response.ok
            );
          }
          
          return response;
        } catch (error) {
          lastError = error as Error;
          
          // 📊 Reportar erro
          const instanceId = this.extractInstanceIdFromUrl(url);
          if (instanceId) {
            this.loadBalancer.reportConnectionEnd(instanceId, 0, false);
          }
          
          if (attempt < maxRetries) {
            // 🔄 Backoff exponencial
            const delay = Math.min(1000 * Math.pow(2, attempt), 5000);
            await new Promise(resolve => setTimeout(resolve, delay));
          }
        }
      }
      
      throw new Error(`Request failed after ${maxRetries} retries: ${lastError.message}`);
    };
  }
  
  // 🔧 Headers padrão
  private getDefaultHeaders(): Record<string, string> {
    return {
      'Content-Type': 'application/json',
      'X-Request-ID': this.generateRequestId(),
      'X-Client-Version': process.env.CLIENT_VERSION || '1.0.0',
      'X-Service-Mesh': 'trpc-microservices',
    };
  }
  
  // 🔌 Parâmetros de conexão WebSocket
  private getConnectionParams(): Record<string, any> {
    return {
      'x-request-id': this.generateRequestId(),
      'x-client-version': process.env.CLIENT_VERSION || '1.0.0',
    };
  }
  
  // 🔍 Extrair instance ID da URL
  private extractInstanceIdFromUrl(url: string): string | null {
    try {
      const urlObj = new URL(url);
      const host = urlObj.hostname;
      const port = urlObj.port;
      return `${host}:${port}`;
    } catch {
      return null;
    }
  }
  
  // 🎲 Gerar request ID único
  private generateRequestId(): string {
    return `req_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
  }
}

// 📁 services/user-service/client.ts
// 🎯 Cliente específico para User Service

import { UserServiceContract } from '@/shared/types/service-contracts';
import { MicroserviceClientFactory } from '@/shared/infrastructure/trpc-microservice-client';

export class UserServiceClient {
  private client: UserServiceContract;
  
  constructor(clientFactory: MicroserviceClientFactory) {
    this.client = clientFactory.createClient<UserServiceContract>('user-service', {
      strategy: 'least-response-time',
      retries: 3,
      enableBatching: true,
    });
  }
  
  // 👤 Operações de usuário
  async getUserById(id: string) {
    return this.client.users.getById(id);
  }
  
  async createUser(data: CreateUserInput) {
    return this.client.users.create(data);
  }
  
  async updateUser(id: string, data: UpdateUserInput) {
    return this.client.users.update(id, data);
  }
  
  async deleteUser(id: string) {
    return this.client.users.delete(id);
  }
  
  async searchUsers(query: SearchUsersInput) {
    return this.client.users.search(query);
  }
  
  // 🔐 Operações de autenticação
  async login(credentials: LoginInput) {
    return this.client.auth.login(credentials);
  }
  
  async refreshToken(token: string) {
    return this.client.auth.refresh(token);
  }
  
  async logout(userId: string) {
    return this.client.auth.logout(userId);
  }
  
  async validateToken(token: string) {
    return this.client.auth.validateToken(token);
  }
}

// 📁 shared/infrastructure/service-orchestrator.ts
// 🎯 Orquestrador de serviços para operações complexas

export class ServiceOrchestrator {
  private userClient: UserServiceClient;
  private orgClient: OrganizationServiceClient;
  private notificationClient: NotificationServiceClient;
  private analyticsClient: AnalyticsServiceClient;
  
  constructor(clientFactory: MicroserviceClientFactory) {
    this.userClient = new UserServiceClient(clientFactory);
    this.orgClient = new OrganizationServiceClient(clientFactory);
    this.notificationClient = new NotificationServiceClient(clientFactory);
    this.analyticsClient = new AnalyticsServiceClient(clientFactory);
  }
  
  // 🎯 Operação orquestrada: Onboarding completo
  async completeUserOnboarding(data: {
    userData: CreateUserInput;
    organizationData: CreateOrgInput;
    invitations?: string[];
  }): Promise<{
    user: User;
    organization: Organization;
    invitationResults: any[];
  }> {
    try {
      // 📊 Track início do onboarding
      await this.analyticsClient.track({
        event: 'onboarding_started',
        userId: null,
        properties: {
          hasInvitations: !!data.invitations?.length,
          timestamp: new Date().toISOString(),
        },
      });
      
      // 1️⃣ Criar organização primeiro
      const organization = await this.orgClient.create(data.organizationData);
      
      // 2️⃣ Criar usuário e associar à organização
      const user = await this.userClient.createUser({
        ...data.userData,
        organizationId: organization.id,
        role: 'OWNER', // Primeiro usuário é owner
      });
      
      // 3️⃣ Enviar notificações de boas-vindas
      const welcomePromise = this.notificationClient.send({
        type: 'welcome',
        userId: user.id,
        templateId: 'user-welcome',
        data: {
          userName: user.name,
          organizationName: organization.name,
        },
      });
      
      // 4️⃣ Processar convites (se houver)
      const invitationResults = [];
      if (data.invitations?.length) {
        for (const email of data.invitations) {
          try {
            const inviteResult = await this.orgClient.inviteMember({
              organizationId: organization.id,
              email,
              role: 'MEMBER',
              invitedBy: user.id,
            });
            invitationResults.push({ email, success: true, result: inviteResult });
          } catch (error) {
            invitationResults.push({ email, success: false, error: error.message });
          }
        }
      }
      
      // 5️⃣ Track conclusão do onboarding
      await this.analyticsClient.track({
        event: 'onboarding_completed',
        userId: user.id,
        properties: {
          organizationId: organization.id,
          invitationsSent: invitationResults.length,
          invitationsSuccessful: invitationResults.filter(r => r.success).length,
          timestamp: new Date().toISOString(),
        },
      });
      
      // 6️⃣ Aguardar notificação de boas-vindas
      await welcomePromise;
      
      return {
        user,
        organization,
        invitationResults,
      };
    } catch (error) {
      // 📊 Track erro no onboarding
      await this.analyticsClient.track({
        event: 'onboarding_failed',
        userId: null,
        properties: {
          error: error.message,
          step: 'unknown',
          timestamp: new Date().toISOString(),
        },
      });
      
      throw error;
    }
  }
  
  // 🎯 Operação orquestrada: Migração de dados
  async migrateUserData(fromUserId: string, toUserId: string): Promise<void> {
    // Implementação de migração complexa entre serviços
    // com compensação em caso de falha
  }
  
  // 🎯 Operação orquestrada: Análise de performance
  async generatePerformanceReport(organizationId: string): Promise<PerformanceReport> {
    // Agregação de dados de múltiplos serviços
    const [userMetrics, orgMetrics, analyticsData] = await Promise.all([
      this.userClient.getMetrics(organizationId),
      this.orgClient.getMetrics(organizationId),
      this.analyticsClient.getMetrics({
        organizationId,
        period: '30d',
        metrics: ['usage', 'performance', 'errors'],
      }),
    ]);
    
    return {
      organizationId,
      period: '30d',
      users: userMetrics,
      organization: orgMetrics,
      analytics: analyticsData,
      generatedAt: new Date().toISOString(),
    };
  }
}

🌐 API Gateway com tRPC

api-gateway/src/server.ts
// 📁 api-gateway/src/server.ts
import express from 'express';
import { createExpressMiddleware } from '@trpc/server/adapters/express';
import { createContext } from './context';
import { appRouter } from './router';
import { ServiceOrchestrator } from '@/shared/infrastructure/service-orchestrator';
import { rateLimitMiddleware } from './middleware/rate-limit';
import { authMiddleware } from './middleware/auth';
import { loggingMiddleware } from './middleware/logging';
import { corsMiddleware } from './middleware/cors';

// 🌐 API Gateway principal
class APIGateway {
  private app: express.Application;
  private orchestrator: ServiceOrchestrator;
  
  constructor() {
    this.app = express();
    this.setupMiddleware();
    this.setupRoutes();
  }
  
  private setupMiddleware(): void {
    // 📊 Middleware de logging
    this.app.use(loggingMiddleware);
    
    // 🌍 CORS
    this.app.use(corsMiddleware);
    
    // 📊 Rate limiting
    this.app.use(rateLimitMiddleware);
    
    // 🔐 Authentication (exceto rotas públicas)
    this.app.use('/trpc', (req, res, next) => {
      const publicPaths = [
        '/health',
        '/auth.login',
        '/auth.register',
        '/public.',
      ];
      
      const isPublicPath = publicPaths.some(path => 
        req.path.includes(path)
      );
      
      if (isPublicPath) {
        return next();
      }
      
      return authMiddleware(req, res, next);
    });
  }
  
  private setupRoutes(): void {
    // 🏥 Health check
    this.app.get('/health', (req, res) => {
      res.json({
        status: 'healthy',
        timestamp: new Date().toISOString(),
        version: process.env.VERSION || '1.0.0',
        uptime: process.uptime(),
      });
    });
    
    // 📊 Métricas
    this.app.get('/metrics', async (req, res) => {
      try {
        const metrics = await this.collectMetrics();
        res.json(metrics);
      } catch (error) {
        res.status(500).json({ error: 'Failed to collect metrics' });
      }
    });
    
    // 🎯 tRPC Router principal
    this.app.use(
      '/trpc',
      createExpressMiddleware({
        router: appRouter,
        createContext,
        onError: ({ error, type, path, input, ctx, req }) => {
          console.error(`❌ tRPC Error [${type}] ${path}:`, error);
          
          // 📊 Log error para monitoring
          if (ctx?.logger) {
            ctx.logger.error('tRPC Error', {
              type,
              path,
              error: error.message,
              input: JSON.stringify(input),
              userId: ctx.session?.user?.id,
              requestId: req?.headers['x-request-id'],
            });
          }
        },
      })
    );
    
    // 🔄 Proxy para serviços específicos (fallback)
    this.app.use('/services/:serviceName/*', this.createServiceProxy());
  }
  
  // 🔄 Criar proxy para serviços
  private createServiceProxy() {
    return async (req: express.Request, res: express.Response) => {
      const { serviceName } = req.params;
      const path = req.params[0];
      
      try {
        const instance = await this.orchestrator.loadBalancer.selectInstance(
          serviceName,
          'least-response-time'
        );
        
        if (!instance) {
          return res.status(503).json({
            error: 'Service unavailable',
            service: serviceName,
          });
        }
        
        const targetUrl = `${instance.protocol}://${instance.host}:${instance.port}/${path}`;
        
        // 🔄 Forward request
        const response = await fetch(targetUrl, {
          method: req.method,
          headers: {
            ...req.headers,
            'host': `${instance.host}:${instance.port}`,
          },
          body: req.method !== 'GET' ? JSON.stringify(req.body) : undefined,
        });
        
        const data = await response.json();
        res.status(response.status).json(data);
      } catch (error) {
        res.status(500).json({
          error: 'Proxy error',
          message: error.message,
        });
      }
    };
  }
  
  // 📊 Coletar métricas do gateway
  private async collectMetrics(): Promise<any> {
    const memUsage = process.memoryUsage();
    
    return {
      timestamp: new Date().toISOString(),
      uptime: process.uptime(),
      memory: {
        rss: Math.round(memUsage.rss / 1024 / 1024),
        heapTotal: Math.round(memUsage.heapTotal / 1024 / 1024),
        heapUsed: Math.round(memUsage.heapUsed / 1024 / 1024),
        external: Math.round(memUsage.external / 1024 / 1024),
      },
      loadBalancer: this.orchestrator.loadBalancer.getStats(),
      services: await this.getServiceHealth(),
    };
  }
  
  // 🏥 Verificar saúde dos serviços
  private async getServiceHealth(): Promise<any> {
    const services = await this.orchestrator.serviceRegistry.getAllServices();
    
    const healthChecks = await Promise.allSettled(
      services.map(async service => {
        try {
          const healthUrl = `${service.protocol}://${service.host}:${service.port}/health`;
          const response = await fetch(healthUrl, { timeout: 5000 });
          
          return {
            name: service.name,
            id: service.id,
            status: response.ok ? 'healthy' : 'unhealthy',
            responseTime: response.headers.get('x-response-time'),
          };
        } catch {
          return {
            name: service.name,
            id: service.id,
            status: 'unhealthy',
            responseTime: null,
          };
        }
      })
    );
    
    return healthChecks.map(result => 
      result.status === 'fulfilled' ? result.value : null
    ).filter(Boolean);
  }
  
  start(port: number): void {
    this.app.listen(port, () => {
      console.log(`🌐 API Gateway listening on port ${port}`);
      console.log(`📊 Health check: http://localhost:${port}/health`);
      console.log(`🎯 tRPC endpoint: http://localhost:${port}/trpc`);
    });
  }
}

// 📁 api-gateway/src/router.ts
// 🎯 Router principal do API Gateway

import { router } from './trpc';
import { userRouter } from './routers/user-proxy';
import { organizationRouter } from './routers/organization-proxy';
import { notificationRouter } from './routers/notification-proxy';
import { analyticsRouter } from './routers/analytics-proxy';
import { healthRouter } from './routers/health';

export const appRouter = router({
  // 🏥 Health checks e sistema
  health: healthRouter,
  
  // 👤 Proxy para User Service
  user: userRouter,
  auth: userRouter.auth,
  
  // 🏢 Proxy para Organization Service
  organization: organizationRouter,
  billing: organizationRouter.billing,
  
  // 📧 Proxy para Notification Service
  notification: notificationRouter,
  push: notificationRouter.push,
  
  // 📊 Proxy para Analytics Service
  analytics: analyticsRouter,
  realtime: analyticsRouter.realtime,
  
  // 🎯 Operações orquestradas
  orchestration: router({
    onboardUser: orchestrationProcedure
      .input(onboardUserSchema)
      .mutation(async ({ input, ctx }) => {
        return ctx.orchestrator.completeUserOnboarding(input);
      }),
      
    generateReport: orchestrationProcedure
      .input(z.object({ organizationId: z.string() }))
      .query(async ({ input, ctx }) => {
        return ctx.orchestrator.generatePerformanceReport(input.organizationId);
      }),
      
    migrateUserData: orchestrationProcedure
      .input(z.object({ 
        fromUserId: z.string(), 
        toUserId: z.string() 
      }))
      .mutation(async ({ input, ctx }) => {
        return ctx.orchestrator.migrateUserData(input.fromUserId, input.toUserId);
      }),
  }),
});

export type AppRouter = typeof appRouter;

// 🚀 Start server
if (require.main === module) {
  const gateway = new APIGateway();
  const port = parseInt(process.env.PORT || '3000');
  gateway.start(port);
}

💡 Melhores Práticas para Microserviços com tRPC

Contratos Bem Definidos:Use interfaces TypeScript para definir contratos claros entre serviços.

Service Discovery Automático:Implemente discovery dinâmico com health checks e failover automático.

Load Balancing Inteligente:Use métricas reais de performance para distribuição de carga.

Observabilidade Completa:Monitore métricas, logs e traces distribuídos entre todos os serviços.