Microsserviços em JavaScript: arquitetura e prática de nível avançado

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Microsserviços em JavaScript: do monolito ao sistema distribuído



A transição de uma aplicação monolítica para uma arquitetura de microsserviços não é apenas uma refatoração técnica, mas uma mudança na filosofia de desenvolvimento. No nível avançado de trabalho com Node.js, enfrentamos a necessidade não apenas de dividir o código em módulos, mas de criar um sistema distribuído estável, escalável e tolerante a falhas. Neste artigo, analisaremos as principais decisões arquiteturais e padrões práticos que o ajudarão a construir microsserviços prontos para produção em JavaScript.



Os principais desafios que o desenvolvedor enfrenta: sincronização de dados entre serviços, manutenção da integridade das transações, descoberta de serviços e comunicação eficiente. Um erro na fase de design pode levar ao caos na produção. Vamos ver como evitar isso.



1. Comunicação entre serviços: RabbitMQ e gRPC



No mundo dos microsserviços, a escolha do protocolo de comunicação é fundamental. Para Node.js, as duas abordagens mais populares são: fila de mensagens assíncrona (RabbitMQ) e RPC síncrono de alto desempenho (gRPC).



Fila assíncrona (RabbitMQ)

Usada para arquitetura orientada a eventos. O serviço produtor não espera uma resposta, mas publica um evento. Isso reduz o acoplamento. Vejamos um exemplo de envio de um pedido para o serviço de entrega.



// Serviço "Pedidos" — produtorconst amqp = require('amqplib');

async function publishOrderCreated(order) { const connection = await amqp.connect('amqp://rabbitmq:5672'); const channel = await connection.createChannel(); const exchange = 'order.events'; await channel.assertExchange(exchange, 'topic', { durable: true });

channel.publish(exchange, 'order.created', Buffer.from(JSON.stringify(order)), { persistent: true });

console.log(`[x] Sent order: ${order.id}`); await channel.close(); await connection.close();}

// Chamada ao criar o pedidopublishOrderCreated({ id: '123', userId: '456', amount: 1500 });


O serviço consumidor (entrega) se inscreve na fila:



// Serviço "Entrega" — consumidorconst amqp = require('amqplib');

async function consumeOrders() { const connection = await amqp.connect('amqp://rabbitmq:5672'); const channel = await connection.createChannel(); const exchange = 'order.events'; await channel.assertExchange(exchange, 'topic', { durable: true });

const q = await channel.assertQueue('', { exclusive: true }); await channel.bindQueue(q.queue, exchange, 'order.created');

channel.consume(q.queue, (msg) => { if (msg.content) { const order = JSON.parse(msg.content.toString()); console.log(`[x] Received order: ${order.id}`); // Lógica de criação da entrega channel.ack(msg); } });}

consumeOrders();


gRPC síncrono

Para cenários que exigem resposta instantânea (por exemplo, verificação de saldo do usuário), o gRPC oferece tipagem estrita e alta velocidade via HTTP/2. Definimos o arquivo proto:



// user.protoservice UserService {  rpc GetUserBalance (UserRequest) returns (UserResponse);}

message UserRequest { string user_id = 1;}

message UserResponse { double balance = 1;}


Implementação do servidor em Node.js:



const grpc = require('@grpc/grpc-js');const protoLoader = require('@grpc/proto-loader');const PROTO_PATH = './user.proto';

const packageDefinition = protoLoader.loadSync(PROTO_PATH, { keepCase: true, longs: String, enums: String, defaults: true, oneofs: true});const userProto = grpc.loadPackageDefinition(packageDefinition).userservice;

function getBalance(call, callback) { const userId = call.request.user_id; // Simulação de consulta ao banco de dados const balance = userId === '456' ? 5000 : 0; callback(null, { balance });}

const server = new grpc.Server();server.addSe

Blogs

Book Recommendations