APIs REST e Bounded Contexts
Implementando Payment, Notification e Analytics
Você implementa APIs iniciais em Go, Ruby e Node para completar o ecossistema de cinco serviços.
Nesta aula você vai
- Criar endpoints reais para Payment, Notification e Analytics
- Padronizar payload e respostas entre stacks diferentes
- Deixar os serviços prontos para integração por eventos
Implementando Payment, Notification e Analytics
Objetivos
- Criar endpoints reais para Payment, Notification e Analytics
- Padronizar payload e respostas entre stacks diferentes
- Deixar os serviços prontos para integração por eventos
Pré-requisitos
customer-serviceeorder-servicejá implementados- Docker Compose operacional
- Familiaridade com APIs REST
Conceito
Microsserviço "vazio" é dívida técnica disfarçada. Mesmo uma API simples precisa nascer com contrato claro, validação mínima e logs consistentes. Se payment, notification e analytics começarem sem padrão, o fluxo distribuído quebra quando os eventos chegarem.
Ao implementar os três serviços agora, você fecha o ecossistema base do e-commerce. Isso permite testar fluxos reais fim a fim e evita desenvolver integração assíncrona em cima de stubs frágeis.
Nesta aula, cada stack recebe código funcional de produção didática: Go Gin no pagamento, Ruby Sinatra para notificação e Node Express para analytics.
Estrutura de arquivos
services/
payment-service/
cmd/api/main.go
notification-service/
app.rb
analytics-service/
src/server.js
Passo a passo
- Implementar API de pagamento em Go (
payment-service/cmd/api/main.go)
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
type CreatePaymentRequest struct {
OrderID string `json:"orderId" binding:"required"`
Amount float64 `json:"amount" binding:"required,gt=0"`
}
func main() {
router := gin.Default()
router.GET("/health", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"status": "ok", "service": "payment-service"})
})
router.POST("/payments", func(c *gin.Context) {
var req CreatePaymentRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "payload inválido"})
return
}
c.JSON(http.StatusCreated, gin.H{
"paymentId": "pay-" + req.OrderID,
"orderId": req.OrderID,
"status": "PROCESSING",
})
})
router.Run(":8080")
}
- Implementar API de notificação em Ruby (
notification-service/app.rb)
require "sinatra"
require "json"
set :port, 4567
set :bind, "0.0.0.0"
NOTIFICATIONS = []
get "/health" do
content_type :json
{ status: "ok", service: "notification-service" }.to_json
end
post "/notifications" do
payload = JSON.parse(request.body.read)
halt 400, { error: "customerId obrigatório" }.to_json unless payload["customerId"]
halt 400, { error: "message obrigatório" }.to_json unless payload["message"]
item = { id: "not-#{NOTIFICATIONS.length + 1}", customerId: payload["customerId"], message: payload["message"] }
NOTIFICATIONS << item
status 201
item.to_json
end
- Implementar API de analytics em Node (
analytics-service/src/server.js)
const express = require("express");
const app = express();
app.use(express.json());
const counters = {};
app.get("/health", (_req, res) => {
res.json({ status: "ok", service: "analytics-service" });
});
app.post("/analytics/events", (req, res) => {
const { type } = req.body;
if (!type) return res.status(400).json({ error: "type é obrigatório" });
counters[type] = (counters[type] || 0) + 1;
return res.status(202).json({ received: true, type, total: counters[type] });
});
app.get("/analytics/events/:type", (req, res) => {
const total = counters[req.params.type] || 0;
res.json({ type: req.params.type, total });
});
app.listen(3000, "0.0.0.0");
- Ajustar
docker-compose.ymlcom portas desses serviços
payment-service:
build: ../services/payment-service
ports: ["8080:8080"]
notification-service:
build: ../services/notification-service
ports: ["4567:4567"]
analytics-service:
build: ../services/analytics-service
ports: ["3000:3000"]
Como testar
- Subir serviços:
docker compose -f infra/docker-compose.yml up --build -d payment-service notification-service analytics-service
- Testar pagamento:
curl -s -X POST http://localhost:8080/payments \
-H "Content-Type: application/json" \
-d '{"orderId":"ord-100","amount":89.9}'
Esperado: status: "PROCESSING".
- Testar notificação:
curl -s -X POST http://localhost:4567/notifications \
-H "Content-Type: application/json" \
-d '{"customerId":"cli-10","message":"Pagamento recebido"}'
- Testar analytics:
curl -s -X POST http://localhost:3000/analytics/events \
-H "Content-Type: application/json" \
-d '{"type":"payment.approved"}'
curl -s http://localhost:3000/analytics/events/payment.approved
Esperado: total maior que zero.
Dicas de projeto
- Padronize
serviceestatusnos endpoints de saúde. - Use JSON em todas as respostas para consistência operacional.
- Mantenha validação mínima de payload desde o início.
- Inclua exemplos de
curlno README de cada serviço.
Erros comuns
- Misturar regra de notificação dentro do serviço de pagamento.
- Retornar status HTTP incorreto em erro de validação.
- Criar API de analytics sem idempotência mínima.
- Ignorar logs de entrada/saída em serviços críticos.
Resumo
Os três serviços complementares agora têm APIs funcionais e testáveis, com padrões consistentes de entrada e saída. A arquitetura está pronta para entrar no módulo de Kafka e fluxos orientados a eventos.