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
- RESTful Web Services vs. Task-Based APIs — Guia da Microsoft comparando estilos de design de API, incluindo orientação a recursos e capacidades
- Capability-Based Security in Distributed Systems — Artigo seminal de Dennis e Van Horn sobre segurança baseada em capacidades
- CQRS Pattern: Command and Query Responsibility Segregation — Martin Fowler explica a separação entre comandos (capacidades) e consultas (recursos)
- REST vs. RPC: Architectural Styles for APIs — Livro que aborda as diferenças fundamentais entre estilos arquiteturais de API
- Designing Data-Intensive Applications — Capítulo sobre arquiteturas orientadas a serviços, incluindo comparações entre modelos de recursos e capacidades
- API Design Patterns: Resources vs. Actions — Manning Publications aborda trade-offs práticos entre modelagem baseada em recursos e ações
- OAuth 2.0 for First-Party Applications — Especificação IETF sobre delegação de capacidades em sistemas de autorização