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.
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.
// 📁 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;
};
};
}
// 📁 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();
}
}
// 📁 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,
};
}
}
// 📁 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/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);
}
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.