<h2>Fundamentos de Microsserviços</h2>
<p>Microsserviços representam um padrão arquitetural onde uma aplicação é construída como um conjunto de serviços pequenos, independentes e que se comunicam entre si. Diferente da arquitetura monolítica tradicional, cada microsserviço executa um processo específico do negócio e pode ser desenvolvido, implantado e escalado de forma independente.</p>
<p>A principal vantagem está na autonomia: equipes podem trabalhar em serviços diferentes simultaneamente usando tecnologias distintas, desde que respeitem os contratos de comunicação. Isso acelera o desenvolvimento e facilita a manutenção. Porém, essa abordagem introduz complexidade na comunicação entre serviços, gerenciamento de dados distribuídos e monitoramento.</p>
<h3>Características Essenciais</h3>
<ul>
<li><strong>Independência</strong>: Cada serviço possui seu próprio banco de dados e ciclo de vida</li>
<li><strong>Comunicação</strong>: Utilizam protocolos leves como HTTP/REST, gRPC ou mensageria assíncrona</li>
<li><strong>Descentralização</strong>: Governança e dados descentralizados por domínio</li>
<li><strong>Resiliência</strong>: Falhas isoladas não comprometem todo o sistema</li>
<li><strong>Escalabilidade</strong>: Escala horizontal independente por serviço</li>
</ul>
<h2>Implementação Prática com Node.js</h2>
<p>Vamos construir dois microsserviços básicos: um para gerenciar usuários e outro para pedidos, comunicando-se via HTTP.</p>
<h3>Serviço de Usuários</h3>
<pre><code class="language-javascript">// users-service/index.js
const express = require('express');
const app = express();
app.use(express.json());
const users = new Map([
[1, { id: 1, name: 'João Silva', email: 'joao@email.com' }],
[2, { id: 2, name: 'Maria Santos', email: 'maria@email.com' }]
]);
app.get('/users/:id', (req, res) => {
const user = users.get(parseInt(req.params.id));
if (!user) return res.status(404).json({ error: 'Usuário não encontrado' });
res.json(user);
});
app.post('/users', (req, res) => {
const { name, email } = req.body;
const id = users.size + 1;
const newUser = { id, name, email };
users.set(id, newUser);
res.status(201).json(newUser);
});
const PORT = 3001;
app.listen(PORT, () => console.log(Users service running on port ${PORT}));</code></pre>
<h3>Serviço de Pedidos</h3>
<pre><code class="language-javascript">// orders-service/index.js
const express = require('express');
const axios = require('axios');
const app = express();
app.use(express.json());
const orders = new Map();
app.post('/orders', async (req, res) => {
try {
const { userId, product, amount } = req.body;
// Comunicação entre microsserviços
const userResponse = await axios.get(http://localhost:3001/users/${userId});
const user = userResponse.data;
const orderId = orders.size + 1;
const order = {
id: orderId,
userId,
userName: user.name,
product,
amount,
status: 'pending'
};
orders.set(orderId, order);
res.status(201).json(order);
} catch (error) {
if (error.response?.status === 404) {
return res.status(400).json({ error: 'Usuário não existe' });
}
res.status(500).json({ error: 'Erro ao criar pedido' });
}
});
app.get('/orders/:id', (req, res) => {
const order = orders.get(parseInt(req.params.id));
if (!order) return res.status(404).json({ error: 'Pedido não encontrado' });
res.json(order);
});
const PORT = 3002;
app.listen(PORT, () => console.log(Orders service running on port ${PORT}));</code></pre>
<p>Este exemplo demonstra comunicação síncrona entre serviços. O serviço de pedidos valida a existência do usuário antes de criar um pedido, ilustrando dependência entre microsserviços.</p>
<h2>Padrões Avançados e Boas Práticas</h2>
<h3>API Gateway</h3>
<p>Um API Gateway atua como ponto de entrada único, roteando requisições para os microsserviços apropriados. Implementação básica:</p>
<pre><code class="language-javascript">// api-gateway/index.js
const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');
const app = express();
// Roteamento para microsserviços
app.use('/api/users', createProxyMiddleware({
target: 'http://localhost:3001',
pathRewrite: { '^/api/users': '/users' },
changeOrigin: true
}));
app.use('/api/orders', createProxyMiddleware({
target: 'http://localhost:3002',
pathRewrite: { '^/api/orders': '/orders' },
changeOrigin: true
}));
// Autenticação centralizada
app.use((req, res, next) => {
const token = req.headers['authorization'];
if (!token && req.path.includes('/orders')) {
return res.status(401).json({ error: 'Token necessário' });
}
next();
});
const PORT = 3000;
app.listen(PORT, () => console.log(API Gateway on port ${PORT}));</code></pre>
<h3>Circuit Breaker Pattern</h3>
<p>Protege o sistema contra falhas em cascata, evitando chamadas repetidas a serviços que estão falhando:</p>
<pre><code class="language-javascript">const axios = require('axios');
class CircuitBreaker {
constructor(threshold = 5, timeout = 60000) {
this.failureCount = 0;
this.threshold = threshold;
this.timeout = timeout;
this.state = 'CLOSED'; // CLOSED, OPEN, HALF_OPEN
this.nextAttempt = Date.now();
}
async call(url) {
if (this.state === 'OPEN') {
if (Date.now() < this.nextAttempt) {
throw new Error('Circuit breaker está OPEN');
}
this.state = 'HALF_OPEN';
}
try {
const response = await axios.get(url, { timeout: 3000 });
this.onSuccess();
return response.data;
} catch (error) {
this.onFailure();
throw error;
}
}
onSuccess() {
this.failureCount = 0;
this.state = 'CLOSED';
}
onFailure() {
this.failureCount++;
if (this.failureCount >= this.threshold) {
this.state = 'OPEN';
this.nextAttempt = Date.now() + this.timeout;
console.log('Circuit breaker OPEN - suspendendo chamadas');
}
}
}
// Uso prático
const breaker = new CircuitBreaker(3, 30000);
async function getUserWithProtection(userId) {
try {
return await breaker.call(http://localhost:3001/users/${userId});
} catch (error) {
// Retorna dados em cache ou resposta padrão
return { id: userId, name: 'Usuário Indisponível' };
}
}</code></pre>
<h3>Mensageria Assíncrona</h3>
<p>Para operações que não exigem resposta imediata, use mensageria. Exemplo com RabbitMQ:</p>
<pre><code class="language-javascript">const amqp = require('amqplib');
// Producer (Orders Service)
async function publishOrderCreated(order) {
const connection = await amqp.connect('amqp://localhost');
const channel = await connection.createChannel();
const queue = 'order_created';
await channel.assertQueue(queue, { durable: true });
channel.sendToQueue(queue, Buffer.from(JSON.stringify(order)), {
persistent: true
});
console.log('Evento order_created publicado:', order.id);
setTimeout(() => connection.close(), 500);
}
// Consumer (Notifications Service)
async function consumeOrderEvents() {
const connection = await amqp.connect('amqp://localhost');
const channel = await connection.createChannel();
const queue = 'order_created';
await channel.assertQueue(queue, { durable: true });
channel.prefetch(1);
console.log('Aguardando eventos de pedidos...');
channel.consume(queue, (msg) => {
const order = JSON.parse(msg.content.toString());
console.log(Enviando notificação para pedido ${order.id});
// Simula envio de email/SMS
setTimeout(() => {
console.log(Notificação enviada: Pedido ${order.id} criado);
channel.ack(msg);
}, 1000);
});
}</code></pre>
<h3>Service Discovery e Health Checks</h3>
<p>Microsserviços precisam se registrar e descobrir outros serviços dinamicamente:</p>
<pre><code class="language-javascript">// health-check endpoint
app.get('/health', (req, res) => {
const health = {
uptime: process.uptime(),
timestamp: Date.now(),
status: 'UP',
checks: {
database: 'UP',
memory: process.memoryUsage().heapUsed < 500000000 ? 'UP' : 'DOWN'
}
};
const statusCode = health.checks.database === 'UP' ? 200 : 503;
res.status(statusCode).json(health);
});</code></pre>
<h2>Observabilidade e Monitoramento</h2>
<p>Em ambientes distribuídos, rastreabilidade é crucial. Implemente logging estruturado e tracing distribuído:</p>
<pre><code class="language-javascript">const winston = require('winston');
const { v4: uuidv4 } = require('uuid');
// Logger estruturado
const logger = winston.createLogger({
format: winston.format.combine(
winston.format.timestamp(),
winston.format.json()
),
transports: [
new winston.transports.File({ filename: 'service.log' })
]
});
// Middleware para correlation ID
app.use((req, res, next) => {
req.correlationId = req.headers['x-correlation-id'] || uuidv4();
res.setHeader('x-correlation-id', req.correlationId);
logger.info('Request received', {
correlationId: req.correlationId,
method: req.method,
path: req.path,
service: 'orders-service'
});
next();
});
// Propagar correlation ID entre serviços
async function callUserService(userId, correlationId) {
return axios.get(http://localhost:3001/users/${userId}, {
headers: { 'x-correlation-id': correlationId }
});
}</code></pre>
<h2>Conclusão</h2>
<ul>
<li>Microsserviços oferecem escalabilidade e independência, mas introduzem complexidade na comunicação, consistência de dados e observabilidade que deve ser cuidadosamente gerenciada</li>
<li>Implemente padrões como API Gateway, Circuit Breaker e mensageria assíncrona para construir sistemas resilientes e preparados para produção</li>
<li>Invista em observabilidade desde o início: logging estruturado, correlation IDs e health checks são essenciais para operar microsserviços com confiança</li>
</ul>
<h2>Referências</h2>
<ul>
<li><a href="https://microservices.io/patterns/microservices.html" target="_blank" rel="noopener noreferrer">Microservices Pattern - Chris Richardson</a></li>
<li><a href="https://www.oreilly.com/library/view/building-microservices-2nd/9781492034018/" target="_blank" rel="noopener noreferrer">Building Microservices - Sam Newman, O'Reilly</a></li>
<li><a href="https://github.com/goldbergyoni/nodebestpractices#4-microservices" target="_blank" rel="noopener noreferrer">Node.js Best Practices - Microservices</a></li>
<li><a href="https://martinfowler.com/microservices/" target="_blank" rel="noopener noreferrer">Martin Fowler - Microservices Guide</a></li>
<li><a href="https://www.nginx.com/blog/introduction-to-microservices/" target="_blank" rel="noopener noreferrer">NGINX Microservices Reference Architecture</a></li>
</ul>