Arquitetura orientada a capacidades vs orientada a recursos

1. Fundamentos e Definições

A arquitetura orientada a recursos, popularizada pelo estilo REST, organiza sistemas em torno de substantivos — entidades que o sistema possui. Cada recurso é identificado por uma URI única e manipulado através de métodos HTTP padronizados (GET, POST, PUT, DELETE). O foco está no estado e na representação dos dados.

Exemplo de modelo mental: "O sistema tem uma coleção de usuários, um catálogo de produtos, um conjunto de pedidos."

A arquitetura orientada a capacidades, por outro lado, organiza sistemas em torno de verbos — ações que o sistema pode executar. Cada capacidade representa uma intenção de negócio explícita, como transferirFundos ou aprovarPedido. O foco está no comportamento e nas operações do domínio.

Exemplo de modelo mental: "O sistema pode criar reservas, confirmar pagamentos, notificar clientes."

A diferença filosófica central é: "o que o sistema tem" versus "o que o sistema pode fazer". Enquanto recursos tratam o sistema como um repositório de dados, capacidades tratam o sistema como um executor de ações de negócio.

2. Modelagem de Contratos e Interfaces

Na orientação a recursos, as interfaces são definidas por URIs estáticas e métodos HTTP:

GET    /reservas          -> Listar todas as reservas
GET    /reservas/{id}     -> Obter detalhes de uma reserva
POST   /reservas          -> Criar nova reserva
PUT    /reservas/{id}     -> Substituir reserva existente
PATCH  /reservas/{id}     -> Atualizar parcialmente reserva
DELETE /reservas/{id}     -> Remover reserva

Na orientação a capacidades, as interfaces são comandos explícitos:

criarReserva(dadosReserva)         -> Cria uma nova reserva
confirmarReserva(idReserva)        -> Confirma a reserva pendente
cancelarReserva(idReserva, motivo) -> Cancela reserva com justificativa
reagendarReserva(idReserva, novaData) -> Altera a data da reserva

Trade-offs: Recursos oferecem rigidez previsível — qualquer operação não-CRUD exige adaptações artificiais (como usar PATCH para ações de negócio). Capacidades oferecem flexibilidade semântica — cada comando expressa exatamente a intenção, mas aumentam o número de endpoints ou mensagens.

3. Evolução e Versionamento

Versionar recursos frequentemente quebra contratos. Adicionar um campo obrigatório a um recurso pode exigir uma nova versão da API:

# Versão 1
GET /v1/reservas/{id}
{
  "id": 123,
  "cliente": "João",
  "data": "2024-01-15"
}

# Versão 2 (nova estrutura)
GET /v2/reservas/{id}
{
  "id": 123,
  "cliente": {"nome": "João", "email": "joao@email.com"},
  "data": "2024-01-15",
  "status": "confirmada"
}

Capacidades evoluem adicionando novos comandos sem modificar os existentes:

# Comandos originais
criarReserva
confirmarReserva
cancelarReserva

# Nova capacidade adicionada (sem quebrar clientes existentes)
reagendarReserva

Caso prático: Em um sistema de pedidos, adicionar a capacidade aplicarDescontoPromocional não exige que clientes antigos atualizem seus sistemas. Já adicionar um campo desconto ao recurso pedido pode forçar todos os consumidores a lidar com o novo campo, mesmo que não o utilizem.

4. Segurança e Autorização

No modelo baseado em recursos, o controle de acesso usa RBAC (Role-Based Access Control) com permissões em endpoints:

# Permissões típicas
role: admin
  - GET /reservas/*
  - POST /reservas/*
  - DELETE /reservas/*

role: usuario
  - GET /reservas/{id} (apenas próprias)
  - POST /reservas (apenas criar)

No modelo baseado em capacidades, tokens carregam ações específicas que o portador pode executar:

# Token com capacidades granulares
token_usuario = {
  "capacidades": [
    "criarReserva:propria",
    "cancelarReserva:propria",
    "consultarReserva:propria"
  ]
}

# Token de atendente (pode cancelar qualquer reserva)
token_atendente = {
  "capacidades": [
    "criarReserva:qualquer",
    "cancelarReserva:qualquer",
    "confirmarReserva:qualquer"
  ]
}

Exemplo de delegação: Um cliente pode delegar a capacidade consultarReserva:123 a um assistente, sem expor acesso a todas as suas reservas ou a operações de escrita.

5. Performance e Escalabilidade

Recursos são naturalmente cacheáveis. Respostas GET podem usar ETags e cabeçalhos de cache HTTP:

GET /reservas?status=confirmada
Cache-Control: max-age=300
ETag: "abc123"

Capacidades apresentam desafios de cache. Comandos como transferirFundos não são idempotentes e não devem ser cacheados. No entanto, capacidades de consulta (como consultarSaldo) podem ser cacheadas com estratégias específicas:

# Capacidade de consulta (cacheável)
consultarSaldo(contaId) -> resultado cacheável por 60s

# Capacidade de comando (não cacheável)
transferirFundos(origem, destino, valor) -> sempre executa

Para escalabilidade horizontal, operações atômicas em recursos (como PUT /reservas/{id}) são mais simples de coordenar do que fluxos que exigem múltiplas capacidades encadeadas.

6. Casos de Uso e Decisão Arquitetural

Escolha orientação a recursos quando:
- CRUD simples predomina (catálogos, listas, perfis)
- APIs públicas com ampla adoção (facilidade de aprendizado)
- Cache de leitura é crítico para performance
- Exemplo: API de catálogo de produtos

Escolha orientação a capacidades quando:
- Domínios complexos com regras de negócio intrincadas
- Workflows que exigem validações e estados intermediários
- Sistemas transacionais com atomicidade
- Exemplo: Sistema de processamento de pagamentos

Estratégias híbridas (CQRS): Combinar recursos para leitura e capacidades para escrita:

# Leitura orientada a recursos
GET /reservas?status=pendente

# Escrita orientada a capacidades
comando: confirmarReserva(idReserva)

7. Exemplo Comparativo: Sistema de Reservas

Modelagem orientada a recursos:

# Criar reserva
POST /reservas
Body: { "cliente": "Maria", "data": "2024-06-01", "quarto": 101 }

# Atualizar status (ação de negócio mascarada como PATCH)
PATCH /reservas/456
Body: { "status": "confirmada" }  # Problema: perde semântica de "confirmação"

# Cancelar reserva
DELETE /reservas/456  # Problema: não permite motivo de cancelamento

Modelagem orientada a capacidades:

# Criar reserva
comando: criarReserva(cliente="Maria", data="2024-06-01", quarto=101)

# Confirmar reserva (ação explícita)
comando: confirmarReserva(id=456)

# Cancelar reserva com motivo
comando: cancelarReserva(id=456, motivo="Cliente desistiu")

Análise:
- Manutenibilidade: Capacidades são mais fáceis de manter porque cada comando encapsula regras de negócio específicas. Recursos tendem a acumular lógica condicional dentro dos métodos HTTP.
- Testabilidade: Capacidades podem ser testadas isoladamente como unidades de negócio. Recursos exigem testes de integração mais complexos para validar efeitos colaterais.
- Alinhamento com o negócio: Capacidades mapeiam diretamente para a linguagem do domínio ("confirmar reserva" vs "PATCH com status=confirmada"), facilitando comunicação com stakeholders.

Referências