Ferramentas & Produtividade

Microsserviços: Do Básico ao Avançado

9 min de leitura

Microsserviços: Do Básico ao Avançado

Fundamentos de Microsserviços 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. 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. Características Essenciais Independência: Cada serviço possui seu próprio banco de dados e ciclo de vida Comunicação: Utilizam protocolos leves como HTTP/REST, gRPC ou mensageria assíncrona Descentralização: Governança e dados descentralizados por domínio Resiliência: Falhas isoladas não comprometem todo o sistema Escalabilidade: Escala horizontal independente por serviço Implementação Prática com Node.js Vamos construir dois microsserviços básicos: um para gerenciar usuários e outro para pedidos, comunicando-se via

<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(&#039;express&#039;);

const app = express();

app.use(express.json());

const users = new Map([

[1, { id: 1, name: &#039;João Silva&#039;, email: &#039;joao@email.com&#039; }],

[2, { id: 2, name: &#039;Maria Santos&#039;, email: &#039;maria@email.com&#039; }]

]);

app.get(&#039;/users/:id&#039;, (req, res) =&gt; {

const user = users.get(parseInt(req.params.id));

if (!user) return res.status(404).json({ error: &#039;Usuário não encontrado&#039; });

res.json(user);

});

app.post(&#039;/users&#039;, (req, res) =&gt; {

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, () =&gt; 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(&#039;express&#039;);

const axios = require(&#039;axios&#039;);

const app = express();

app.use(express.json());

const orders = new Map();

app.post(&#039;/orders&#039;, async (req, res) =&gt; {

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: &#039;pending&#039;

};

orders.set(orderId, order);

res.status(201).json(order);

} catch (error) {

if (error.response?.status === 404) {

return res.status(400).json({ error: &#039;Usuário não existe&#039; });

}

res.status(500).json({ error: &#039;Erro ao criar pedido&#039; });

}

});

app.get(&#039;/orders/:id&#039;, (req, res) =&gt; {

const order = orders.get(parseInt(req.params.id));

if (!order) return res.status(404).json({ error: &#039;Pedido não encontrado&#039; });

res.json(order);

});

const PORT = 3002;

app.listen(PORT, () =&gt; 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(&#039;express&#039;);

const { createProxyMiddleware } = require(&#039;http-proxy-middleware&#039;);

const app = express();

// Roteamento para microsserviços

app.use(&#039;/api/users&#039;, createProxyMiddleware({

target: &#039;http://localhost:3001&#039;,

pathRewrite: { &#039;^/api/users&#039;: &#039;/users&#039; },

changeOrigin: true

}));

app.use(&#039;/api/orders&#039;, createProxyMiddleware({

target: &#039;http://localhost:3002&#039;,

pathRewrite: { &#039;^/api/orders&#039;: &#039;/orders&#039; },

changeOrigin: true

}));

// Autenticação centralizada

app.use((req, res, next) =&gt; {

const token = req.headers[&#039;authorization&#039;];

if (!token &amp;&amp; req.path.includes(&#039;/orders&#039;)) {

return res.status(401).json({ error: &#039;Token necessário&#039; });

}

next();

});

const PORT = 3000;

app.listen(PORT, () =&gt; 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(&#039;axios&#039;);

class CircuitBreaker {

constructor(threshold = 5, timeout = 60000) {

this.failureCount = 0;

this.threshold = threshold;

this.timeout = timeout;

this.state = &#039;CLOSED&#039;; // CLOSED, OPEN, HALF_OPEN

this.nextAttempt = Date.now();

}

async call(url) {

if (this.state === &#039;OPEN&#039;) {

if (Date.now() &lt; this.nextAttempt) {

throw new Error(&#039;Circuit breaker está OPEN&#039;);

}

this.state = &#039;HALF_OPEN&#039;;

}

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 = &#039;CLOSED&#039;;

}

onFailure() {

this.failureCount++;

if (this.failureCount &gt;= this.threshold) {

this.state = &#039;OPEN&#039;;

this.nextAttempt = Date.now() + this.timeout;

console.log(&#039;Circuit breaker OPEN - suspendendo chamadas&#039;);

}

}

}

// 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: &#039;Usuário Indisponível&#039; };

}

}</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(&#039;amqplib&#039;);

// Producer (Orders Service)

async function publishOrderCreated(order) {

const connection = await amqp.connect(&#039;amqp://localhost&#039;);

const channel = await connection.createChannel();

const queue = &#039;order_created&#039;;

await channel.assertQueue(queue, { durable: true });

channel.sendToQueue(queue, Buffer.from(JSON.stringify(order)), {

persistent: true

});

console.log(&#039;Evento order_created publicado:&#039;, order.id);

setTimeout(() =&gt; connection.close(), 500);

}

// Consumer (Notifications Service)

async function consumeOrderEvents() {

const connection = await amqp.connect(&#039;amqp://localhost&#039;);

const channel = await connection.createChannel();

const queue = &#039;order_created&#039;;

await channel.assertQueue(queue, { durable: true });

channel.prefetch(1);

console.log(&#039;Aguardando eventos de pedidos...&#039;);

channel.consume(queue, (msg) =&gt; {

const order = JSON.parse(msg.content.toString());

console.log(Enviando notificação para pedido ${order.id});

// Simula envio de email/SMS

setTimeout(() =&gt; {

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(&#039;/health&#039;, (req, res) =&gt; {

const health = {

uptime: process.uptime(),

timestamp: Date.now(),

status: &#039;UP&#039;,

checks: {

database: &#039;UP&#039;,

memory: process.memoryUsage().heapUsed &lt; 500000000 ? &#039;UP&#039; : &#039;DOWN&#039;

}

};

const statusCode = health.checks.database === &#039;UP&#039; ? 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(&#039;winston&#039;);

const { v4: uuidv4 } = require(&#039;uuid&#039;);

// Logger estruturado

const logger = winston.createLogger({

format: winston.format.combine(

winston.format.timestamp(),

winston.format.json()

),

transports: [

new winston.transports.File({ filename: &#039;service.log&#039; })

]

});

// Middleware para correlation ID

app.use((req, res, next) =&gt; {

req.correlationId = req.headers[&#039;x-correlation-id&#039;] || uuidv4();

res.setHeader(&#039;x-correlation-id&#039;, req.correlationId);

logger.info(&#039;Request received&#039;, {

correlationId: req.correlationId,

method: req.method,

path: req.path,

service: &#039;orders-service&#039;

});

next();

});

// Propagar correlation ID entre serviços

async function callUserService(userId, correlationId) {

return axios.get(http://localhost:3001/users/${userId}, {

headers: { &#039;x-correlation-id&#039;: 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&#039;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>

Comentários

Mais em Ferramentas & Produtividade

Como Usar PHP Moderno em Produção
Como Usar PHP Moderno em Produção

Estrutura de Projeto e Padrões Modernos PHP moderno em produção começa com um...

Dominando Git e Versionamento em Projetos Reais
Dominando Git e Versionamento em Projetos Reais

Fundamentos: O que é Git e Por Que Importa Git é um sistema de controle de ve...

O que Todo Dev Deve Saber sobre Event Sourcing
O que Todo Dev Deve Saber sobre Event Sourcing

O que é Event Sourcing Event Sourcing é um padrão arquitetural onde você arma...