Microsserviços em JavaScript: arquitetura e prática com Node.js
A arquitetura de microsserviços tornou-se o padrão de facto para construir aplicações web escaláveis e tolerantes a falhas. JavaScript, especialmente o ambiente de execução Node.js, é ideal para este paradigma graças ao seu modelo assíncrono, ecossistema rico e alta velocidade de desenvolvimento. Neste artigo, vamos analisar como projetar e implementar microsserviços em JavaScript, considerar as principais decisões arquiteturais e escrever exemplos de código funcionais.
1. Fundamentos da arquitetura de microsserviços em Node.js
Microsserviços são uma abordagem onde a aplicação é dividida em serviços pequenos e fracamente acoplados. Cada serviço é responsável por uma função de negócio específica (por exemplo, gestão de utilizadores, processamento de pedidos ou envio de notificações) e pode ser desenvolvido, implantado e escalado de forma independente. O Node.js, com sua arquitetura orientada a eventos, permite criar serviços leves e rápidos que processam eficientemente milhares de requisições simultâneas.
Principais vantagens de usar JavaScript para microsserviços:
- Linguagem única — frontend e backend são escritos na mesma linguagem, facilitando a transição dos desenvolvedores entre as camadas.
- Alta performance — a entrada/saída não bloqueante do Node.js é excelente para microsserviços que frequentemente comunicam pela rede.
- Ecossistema rico — o npm fornece bibliotecas prontas para criar REST APIs, trabalhar com filas, bancos de dados e conteinerização.
2. Projeto e comunicação entre serviços
Ao projetar microsserviços em JavaScript, é importante definir como os serviços se comunicarão entre si. Existem duas abordagens principais:
- Comunicação síncrona (REST/gRPC) — o serviço A envia uma requisição HTTP para o serviço B e aguarda a resposta. Simplicidade de implementação, mas cria um acoplamento forte e pode levar a falhas em cascata.
- Comunicação assíncrona (filas de mensagens) — os serviços trocam mensagens através de um broker (RabbitMQ, Kafka). Isso garante baixo acoplamento, aumenta a tolerância a falhas e permite escalar facilmente os handlers.
Vamos considerar um exemplo de um microsserviço simples para gestão de pedidos usando Express (REST API) e RabbitMQ (envio assíncrono de notificações).
Exemplo: REST API do microsserviço de pedidos
const express = require('express');const amqp = require('amqplib');const app = express();app.use(express.json());
// Armazenamento de pedidos (num projeto real, seria uma base de dados)const orders = [];
// Criação de pedidoapp.post('/orders', async (req, res) => { const { userId, product, quantity } = req.body; const order = { id: Date.now(), userId, product, quantity, status: 'created' }; orders.push(order); // Envia o evento para a fila para notificar o utilizador const connection = await amqp.connect('amqp://localhost'); const channel = await connection.createChannel(); await channel.assertQueue('order_notifications'); channel.sendToQueue('order_notifications', Buffer.from(JSON.stringify(order))); res.status(201).json(order);});
// Obter lista de pedidosapp.get('/orders', (req, res) => { res.json(orders);});
app.listen(3001, () => console.log('Order service running on port 3001'));Exemplo: Serviço de notificações (consumidor assíncrono)
const amqp = require('amqplib');
async function consumeNotifications() { const connection = await amqp.connect('amqp://localhost'); const channel = await connection.createChannel(); await channel.assertQueue('order_notifications'); channel.consume('order_notifications', (msg) => { const order = JSON.parse(msg.content.toString()); console.log(`Sending notification for order ${order.id} to user ${order.userId}`); // Aqui pode ser chamada uma API externa (email,