DevOps

Projeto Capstone: Arquitetura do Sistema Completo Já leu

14 min de leitura

Projeto Capstone: Arquitetura do Sistema Completo
Os quarenta e sete artigos anteriores cobriram, em progressão cuidadosa, cada camada da engenharia de software moderna: o terminal Linux, o controle de versão com Git, containers com Docker, pipelines de CI/CD, infraestr

Os quarenta e sete artigos anteriores cobriram, em progressão cuidadosa, cada camada da engenharia de software moderna: o terminal Linux, o controle de versão com Git, containers com Docker, pipelines de CI/CD, infraestrutura como código com Terraform, monitoramento com Prometheus e Grafana, AWS em profundidade, Kubernetes, segurança com DevSecOps, compliance, FinOps, resiliência e cultura organizacional. Cada artigo tratou de sua camada com profundidade — ferramentas, padrões, código funcional, decisões de arquitetura.

O que falta é a integração. Sistemas reais não são coleções de tecnologias operando em silos — são organismos onde cada componente interage com os demais de maneiras que criam propriedades emergentes, tanto positivas quanto negativas. Um pipeline de CI/CD excelente tem valor limitado se a infraestrutura que ele provisiona não está monitorada. Observabilidade sofisticada não ajuda se o processo de deploy não tem estratégia de rollback. Segurança no código não basta se a infraestrutura está mal configurada.

O projeto capstone desta série integra todos esses componentes em um sistema completo de produção: uma plataforma de e-commerce com microsserviços, deployed no EKS, com pipeline completo de CI/CD, observabilidade full-stack, segurança em camadas e práticas de FinOps. Não é um sistema simplificado para fins didáticos — é a arquitetura que seria usada em produção real, com as decisões de trade-off que sistemas reais exigem.

Este artigo define a arquitetura. Os artigos 49 e 50 implementam a infraestrutura e os serviços. Os artigos 51 e 52 implementam o pipeline completo e as operações em produção.


O Sistema: Plataforma de E-Commerce

A plataforma escolhida para o capstone é um sistema de e-commerce — domínio suficientemente rico para exigir todas as práticas abordadas no curso, mas familiar o bastante para que o foco permaneça nas práticas de engenharia e não na lógica de negócio.

O sistema é composto por cinco microsserviços, cada um com responsabilidade bem definida:

Serviço de Catálogo — gerencia produtos, categorias, preços e estoque. Alta taxa de leitura, baixa taxa de escrita. Candidato ideal para caching agressivo.

Serviço de Usuários — gerencia contas, autenticação e perfis. Dados sensíveis sob LGPD. Requer auditoria de todos os acessos.

Serviço de Pedidos — orquestra o fluxo de compra: criação de carrinho, checkout, pagamento e fulfillment. Core do negócio — requer alta disponibilidade e consistência eventual entre serviços.

Serviço de Notificações — envia emails, SMS e notificações push. Assíncrono por natureza — opera sobre filas SQS. Tolerante a latência, mas não a perda de mensagens.

API Gateway — ponto único de entrada para clientes externos. Roteamento, autenticação, rate limiting e agregação de respostas.


Diagrama de Arquitetura

┌─────────────────────────────────────────────────────────────────────┐
│                           INTERNET                                   │
└──────────────────────────┬──────────────────────────────────────────┘
                           │
                ┌──────────▼──────────┐
                │    CloudFront CDN    │
                │  WAF + Shield Std   │
                └──────────┬──────────┘
                           │
              ┌────────────▼────────────┐
              │  Application LB (ALB)   │
              │  Route53 → api.loja.com │
              └────────────┬────────────┘
                           │
    ┌──────────────────────▼──────────────────────────┐
    │                  EKS Cluster                     │
    │  ┌────────────────────────────────────────────┐ │
    │  │           Namespace: producao               │ │
    │  │                                            │ │
    │  │  ┌────────────┐    ┌──────────────────┐   │ │
    │  │  │ API Gateway│    │ Serviço Catálogo  │   │ │
    │  │  │  (3 pods)  │───▶│    (5 pods)      │   │ │
    │  │  └─────┬──────┘    └────────┬─────────┘   │ │
    │  │        │                    │              │ │
    │  │  ┌─────▼──────┐    ┌────────▼─────────┐   │ │
    │  │  │  Serviço   │    │    Serviço       │   │ │
    │  │  │  Usuários  │    │    Pedidos       │   │ │
    │  │  │  (3 pods)  │    │    (5 pods)      │   │ │
    │  │  └────────────┘    └────────┬─────────┘   │ │
    │  │                             │              │ │
    │  │                    ┌────────▼─────────┐   │ │
    │  │                    │    Serviço       │   │ │
    │  │                    │  Notificações    │   │ │
    │  │                    │    (2 pods)      │   │ │
    │  │                    └──────────────────┘   │ │
    │  └────────────────────────────────────────────┘ │
    │                                                  │
    │  ┌────────────────────────────────────────────┐ │
    │  │          Namespace: monitoring              │ │
    │  │  Prometheus · Grafana · Loki · Tempo        │ │
    │  └────────────────────────────────────────────┘ │
    └──────────────────────────────────────────────────┘
                           │
    ┌──────────────────────▼──────────────────────────┐
    │                 AWS Managed Services             │
    │                                                  │
    │  ┌─────────────┐  ┌──────────────┐              │
    │  │ RDS Postgres │  │ ElastiCache  │              │
    │  │  Multi-AZ   │  │    Redis     │              │
    │  │             │  │  (cluster)   │              │
    │  └─────────────┘  └──────────────┘              │
    │                                                  │
    │  ┌─────────────┐  ┌──────────────┐              │
    │  │  SQS Queues │  │      S3      │              │
    │  │ notificações│  │  assets +    │              │
    │  │   + DLQ     │  │   backups    │              │
    │  └─────────────┘  └──────────────┘              │
    │                                                  │
    │  ┌─────────────┐  ┌──────────────┐              │
    │  │ Secrets Mgr │  │     KMS      │              │
    │  │ credenciais │  │ criptografia │              │
    │  └─────────────┘  └──────────────┘              │
    └──────────────────────────────────────────────────┘

Decisões de Arquitetura e Trade-offs

Toda arquitetura é o resultado de decisões que priorizam algumas propriedades em detrimento de outras. Documentar essas decisões — com a alternativa considerada e o motivo da escolha — é uma prática essencial que o capstone demonstra através de Architecture Decision Records:

ADR-001: Microsserviços vs Monolito Modular

# ADR-001: Adoção de Microsserviços

**Status:** Aceito
**Data:** 2025-01-15
**Decisores:** Time de Arquitetura

## Contexto

O sistema precisa suportar times independentes trabalhando em
diferentes domínios de negócio com ciclos de deploy independentes.
O catálogo tem requisitos de leitura muito diferentes do serviço
de pedidos, que tem requisitos de consistência que o serviço de
notificações não compartilha.

## Decisão

Adotar arquitetura de microsserviços com cinco serviços principais,
cada um com seu próprio banco de dados (database-per-service pattern).

## Alternativa Considerada

Monolito modular — código organizado em módulos com fronteiras claras
mas deployado como uma única unidade. Menor complexidade operacional,
deploy atômico, sem latência de rede entre módulos.

## Motivo da Escolha

A necessidade de deploy independente entre catálogo (deploy a cada
mudança de produto, várias vezes ao dia) e pedidos (deploy mais
controlado, requer testes extensivos) foi o fator determinante.
Times independentes precisam de deployabilidade independente.

## Consequências

Positivas: Deploy independente por serviço, scaling independente,
isolamento de falhas, autonomia de times.

Negativas: Complexidade operacional elevada (requer Kubernetes,
service discovery, tracing distribuído), latência de rede entre
serviços, consistência eventual entre bancos de dados.

## Critério de Revisão

Se o número de microsserviços superar 15 sem crescimento
proporcional do time, reavaliar consolidação de serviços menores.

ADR-002: Comunicação entre Serviços

# ADR-002: Padrão de Comunicação entre Serviços

**Status:** Aceito
**Data:** 2025-01-15

## Contexto

Serviços precisam se comunicar. As opções principais são:
comunicação síncrona via HTTP/gRPC, ou comunicação assíncrona
via mensageria (SQS, Kafka, EventBridge).

## Decisão

Comunicação híbrida:
- **Síncrona (HTTP/REST)** para consultas que precisam de
  resposta imediata: API Gateway → serviços, Pedidos → Catálogo
  (verificar estoque), Pedidos → Usuários (autenticação)
- **Assíncrona (SQS)** para eventos que não requerem resposta
  imediata: Pedidos → Notificações (confirmar pedido),
  Pedidos → Catálogo (atualizar estoque após confirmação)

## Padrão para Comunicação Síncrona

Todos os clientes HTTP inter-serviço usam o ClienteHTTPResilient
com circuit breaker e retry — implementado no Artigo 45.

## Padrão para Comunicação Assíncrona

Produtor publica evento no SQS. Consumidor processa com
visibilidade de 30s e até 3 tentativas antes de mover para DLQ.
Eventos têm schema versionado para compatibilidade.

## Consequências

A combinação evita o acoplamento temporal completo (tudo síncrono)
e a complexidade de tornar tudo assíncrono onde a simplicidade
do request-response é adequada.

ADR-003: Estratégia de Banco de Dados

# ADR-003: Banco de Dados por Serviço

**Status:** Aceito
**Data:** 2025-01-15

## Decisão

Cada microsserviço tem seu próprio banco de dados ou schema isolado.
Nenhum serviço acessa diretamente o banco de outro serviço.

Distribuição dos bancos:
- **Catálogo:** PostgreSQL próprio (leituras intensas, writes moderados)
- **Usuários:** PostgreSQL próprio (dados sensíveis LGPD, acesso auditado)
- **Pedidos:** PostgreSQL próprio (consistência transacional crítica)
- **Notificações:** Sem banco próprio — usa SQS como fonte de verdade

Redis compartilhado para cache — não contém dados de negócio,
apenas cache de leitura com TTL. A perda do Redis é tolerável
(degrada performance, não corrompe dados).

## Alternativa Considerada

Schema por serviço no mesmo banco de dados PostgreSQL — menor
custo de operação, transações entre serviços possíveis.

## Motivo da Rejeição

Schema compartilhado cria acoplamento de infraestrutura: uma
migration mal feita em um serviço pode bloquear os demais.
O isolamento completo tem custo financeiro maior mas garante
autonomia operacional total.

Estrutura do Repositório

O projeto capstone usa um monorepo com fronteiras claras entre serviços:

loja-plataforma/
│
├── services/
│   ├── api-gateway/
│   │   ├── src/
│   │   ├── Dockerfile
│   │   ├── package.json
│   │   └── catalog-info.yaml
│   │
│   ├── catalog-service/
│   │   ├── src/
│   │   ├── Dockerfile
│   │   ├── package.json
│   │   └── catalog-info.yaml
│   │
│   ├── user-service/
│   │   ├── src/
│   │   ├── Dockerfile
│   │   ├── package.json
│   │   └── catalog-info.yaml
│   │
│   ├── order-service/
│   │   ├── src/
│   │   ├── Dockerfile
│   │   ├── package.json
│   │   └── catalog-info.yaml
│   │
│   └── notification-service/
│       ├── src/
│       ├── Dockerfile
│       ├── package.json
│       └── catalog-info.yaml
│
├── infrastructure/
│   ├── terraform/
│   │   ├── environments/
│   │   │   ├── staging/
│   │   │   └── production/
│   │   └── modules/
│   │       ├── eks/
│   │       ├── rds/
│   │       ├── elasticache/
│   │       ├── networking/
│   │       └── observability/
│   └── kubernetes/
│       ├── base/
│       │   ├── namespaces.yaml
│       │   ├── network-policies.yaml
│       │   └── resource-quotas.yaml
│       ├── services/
│       │   ├── api-gateway/
│       │   ├── catalog-service/
│       │   ├── user-service/
│       │   ├── order-service/
│       │   └── notification-service/
│       └── platform/
│           ├── argocd/
│           ├── karpenter/
│           ├── prometheus/
│           └── external-secrets/
│
├── .github/
│   └── workflows/
│       ├── ci-services.yml
│       ├── deploy-infrastructure.yml
│       ├── deploy-services.yml
│       └── security-scan.yml
│
├── docs/
│   ├── adr/               # Architecture Decision Records
│   ├── runbooks/          # Runbooks operacionais
│   └── mkdocs.yml
│
└── scripts/
    ├── local-dev/
    ├── chaos/
    └── finops/

Contratos de API entre Serviços

Os contratos entre serviços são documentados como especificações OpenAPI e mantidos no repositório. Mudanças incompatíveis requerem versionamento da API:

# services/catalog-service/openapi.yaml
openapi: "3.1.0"
info:
  title: Catalog Service API
  version: "2.0.0"
  description: |
    API interna do serviço de catálogo.
    Versão 2.0 — não é compatível com v1 (removido campo `preco_antigo`).

servers:
  - url: http://catalog-service.producao.svc.cluster.local
    description: Cluster interno (DNS Kubernetes)

paths:
  /produtos/{id}:
    get:
      operationId: buscarProduto
      summary: Busca produto por ID
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Produto encontrado
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Produto'
        "404":
          $ref: '#/components/responses/NaoEncontrado'
        "503":
          $ref: '#/components/responses/ServicoIndisponivel'

  /produtos/{id}/estoque:
    get:
      operationId: verificarEstoque
      summary: Verifica disponibilidade de estoque
      description: |
        Endpoint crítico chamado pelo serviço de pedidos durante checkout.
        SLA: p99 < 50ms. Circuit breaker configurado com threshold 5 falhas.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: quantidade
          in: query
          required: true
          schema:
            type: integer
            minimum: 1
      responses:
        "200":
          description: Informação de estoque
          content:
            application/json:
              schema:
                type: object
                properties:
                  disponivel:
                    type: boolean
                  quantidade_disponivel:
                    type: integer
                  reserva_expira_em:
                    type: string
                    format: date-time
                    nullable: true

components:
  schemas:
    Produto:
      type: object
      required: [id, nome, preco, ativo]
      properties:
        id:
          type: string
          format: uuid
        nome:
          type: string
          maxLength: 255
        descricao:
          type: string
          nullable: true
        preco:
          type: number
          format: decimal
          minimum: 0
        preco_promocional:
          type: number
          format: decimal
          nullable: true
        categoria_id:
          type: string
          format: uuid
        ativo:
          type: boolean
        criado_em:
          type: string
          format: date-time
        atualizado_em:
          type: string
          format: date-time

  responses:
    NaoEncontrado:
      description: Recurso não encontrado
      content:
        application/json:
          schema:
            type: object
            properties:
              erro:
                type: string
                example: "Produto não encontrado"
              codigo:
                type: string
                example: "PRODUTO_NAO_ENCONTRADO"

    ServicoIndisponivel:
      description: Serviço temporariamente indisponível
      headers:
        Retry-After:
          schema:
            type: integer
          description: Segundos até tentar novamente

Modelo de Dados

Cada serviço é dono do seu schema. Os schemas são versionados via migrations com Flyway ou equivalente:

-- services/order-service/migrations/V001__criar_tabelas_pedidos.sql

CREATE EXTENSION IF NOT EXISTS "uuid-ossp";

CREATE TABLE pedidos (
  id              UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
  usuario_id      UUID NOT NULL,
  status          VARCHAR(50) NOT NULL DEFAULT 'rascunho',
  subtotal        DECIMAL(12,2) NOT NULL DEFAULT 0,
  desconto        DECIMAL(12,2) NOT NULL DEFAULT 0,
  frete           DECIMAL(12,2) NOT NULL DEFAULT 0,
  total           DECIMAL(12,2) NOT NULL DEFAULT 0,
  moeda           CHAR(3) NOT NULL DEFAULT 'BRL',
  endereco_entrega JSONB NOT NULL DEFAULT '{}',
  metadata        JSONB NOT NULL DEFAULT '{}',
  criado_em       TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  atualizado_em   TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  confirmado_em   TIMESTAMPTZ,
  cancelado_em    TIMESTAMPTZ,

  CONSTRAINT status_valido CHECK (
    status IN (
      'rascunho', 'aguardando_pagamento', 'pago',
      'em_separacao', 'enviado', 'entregue', 'cancelado'
    )
  ),
  CONSTRAINT total_positivo CHECK (total >= 0),
  CONSTRAINT subtotal_positivo CHECK (subtotal >= 0)
);

CREATE TABLE itens_pedido (
  id              UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
  pedido_id       UUID NOT NULL REFERENCES pedidos(id) ON DELETE CASCADE,
  produto_id      UUID NOT NULL,
  -- Snapshot do produto no momento do pedido
  -- Não usa FK para catalog-service — serviços são independentes
  nome_produto    VARCHAR(255) NOT NULL,
  preco_unitario  DECIMAL(12,2) NOT NULL,
  quantidade      INTEGER NOT NULL,
  subtotal        DECIMAL(12,2) NOT NULL,

  CONSTRAINT quantidade_positiva CHECK (quantidade > 0),
  CONSTRAINT preco_positivo CHECK (preco_unitario > 0)
);

CREATE TABLE eventos_pedido (
  id          UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
  pedido_id   UUID NOT NULL REFERENCES pedidos(id),
  tipo        VARCHAR(100) NOT NULL,
  payload     JSONB NOT NULL DEFAULT '{}',
  criado_por  VARCHAR(255),  -- ID do usuário ou 'sistema'
  criado_em   TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

-- Índices para as queries mais comuns
CREATE INDEX idx_pedidos_usuario_id
  ON pedidos(usuario_id)
  WHERE status NOT IN ('cancelado');

CREATE INDEX idx_pedidos_status_criado
  ON pedidos(status, criado_em DESC);

CREATE INDEX idx_itens_pedido_pedido_id
  ON itens_pedido(pedido_id);

CREATE INDEX idx_eventos_pedido_id
  ON eventos_pedido(pedido_id, criado_em DESC);

-- Trigger para atualizar atualizado_em automaticamente
CREATE OR REPLACE FUNCTION atualizar_timestamp()
RETURNS TRIGGER AS $$
BEGIN
  NEW.atualizado_em = NOW();
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER trigger_pedidos_atualizado_em
  BEFORE UPDATE ON pedidos
  FOR EACH ROW EXECUTE FUNCTION atualizar_timestamp();

SLOs do Sistema

Os Service Level Objectives definem o que significa "funcionar bem" para cada serviço:

# kubernetes/platform/slos.yaml
# SLOs definidos usando o formato OpenSLO

apiVersion: openslo/v1
kind: SLO
metadata:
  name: api-gateway-disponibilidade
  namespace: producao
spec:
  service: api-gateway
  description: "Disponibilidade do API Gateway — requisições com sucesso"

  budgetingMethod: Occurrences

  objectives:
    - displayName: "99.5% de disponibilidade"
      target: 0.995
      # Considera falha: status 5xx ou timeout
      indicator:
        ratio:
          good:
            metric:
              prometheusMetric:
                query: |
                  sum(rate(http_requests_total{
                    service="api-gateway",
                    status!~"5.."
                  }[5m]))
          total:
            metric:
              prometheusMetric:
                query: |
                  sum(rate(http_requests_total{
                    service="api-gateway"
                  }[5m]))
---
apiVersion: openslo/v1
kind: SLO
metadata:
  name: order-service-latencia
  namespace: producao
spec:
  service: order-service
  description: "Latência do checkout — p99 abaixo de 1 segundo"

  budgetingMethod: Occurrences

  objectives:
    - displayName: "p99 < 1s para checkout"
      target: 0.99
      indicator:
        ratio:
          good:
            metric:
              prometheusMetric:
                query: |
                  sum(rate(http_request_duration_seconds_bucket{
                    service="order-service",
                    endpoint="/checkout",
                    le="1.0"
                  }[5m]))
          total:
            metric:
              prometheusMetric:
                query: |
                  sum(rate(http_request_duration_seconds_count{
                    service="order-service",
                    endpoint="/checkout"
                  }[5m]))

Plano de Implementação

Os próximos quatro artigos implementam o sistema em etapas incrementais, cada uma entregando valor independente:

Artigo 49 — Infraestrutura: Provisionamento completo do cluster EKS, RDS, ElastiCache, SQS e networking com Terraform. Ao final, a infraestrutura está pronta para receber os serviços.

Artigo 50 — Os Serviços: Implementação dos cinco microsserviços com código funcional, testes, Dockerfiles otimizados e manifestos Kubernetes. Ao final, os serviços rodam localmente com Docker Compose e no cluster com kubectl apply.

Artigo 51 — Pipeline Completo: Pipeline de CI/CD com GitHub Actions que executa testes, scanning de segurança, build de imagem, deploy no EKS via ArgoCD e verificação pós-deploy. Ao final, um push para main resulta automaticamente em deploy em produção.

Artigo 52 — Operações em Produção: Dashboards de observabilidade, alertas calibrados, runbooks, experimentos de chaos, configuração de backups e o postmortem do primeiro incidente simulado. Ao final, o sistema está operacionalmente maduro.


O Que Vem a Seguir

O próximo artigo começa a implementação com o que sustenta tudo: a infraestrutura. O Terraform provisiona o cluster EKS, os bancos de dados, o cache, as filas e toda a rede com os controles de segurança que os artigos anteriores descreveram — desta vez, integrados em um único sistema coerente.


Referências para Aprofundamento

Arquitetura de microsserviços - Microservices Patterns — Chris Richardson — microservices.io — Catálogo completo dos padrões de microsserviços com descrição do problema, solução, exemplos e trade-offs. Referência essencial para qualquer arquiteto trabalhando com microsserviços. - Building Microservices — O'Reilly — oreilly.com — Livro de referência de Sam Newman cobrindo todos os aspectos de microsserviços em produção, da decomposição à operação.

Architecture Decision Records - ADR GitHub — adr.github.io — Repositório de templates e ferramentas para Architecture Decision Records, incluindo o formato MADR e ferramentas de linha de comando para gerenciamento.

OpenSLO - OpenSLO Specification — openslo.com — Especificação aberta para definição de SLOs, com suporte a múltiplos backends de observabilidade e integração com Kubernetes via operadores.

Comentários

Mais em DevOps

Route53, CloudFront e ACM: Rede e Entrega de Conteúdo
Route53, CloudFront e ACM: Rede e Entrega de Conteúdo

Uma aplicação pode estar perfeitamente construída — código limpo, banco de da...

Notificações, Logs e Monitoramento do Pipeline
Notificações, Logs e Monitoramento do Pipeline

Um pipeline de CI/CD que falha sem avisar ninguém é pior do que não ter pipel...

GitLab Self-Hosted: Soberania Total sobre Código e Pipelines
GitLab Self-Hosted: Soberania Total sobre Código e Pipelines

Há contextos em que usar um serviço SaaS para hospedar código e pipelines não...