Decomposição por domínio vs por capacidade técnica
1. Introdução aos Princípios de Decomposição em Arquitetura de Software
Decomposição arquitetural é o processo de dividir um sistema de software em partes menores, gerenciáveis e coesas. Essa divisão determina como os componentes se relacionam, como as equipes trabalham e como o sistema evolui ao longo do tempo. A escolha do critério de decomposição é uma das decisões arquiteturais mais impactantes, pois define a estrutura fundamental do código-fonte, os fluxos de comunicação entre módulos e a facilidade de manutenção.
Dois paradigmas dominam essa discussão: a decomposição por capacidade técnica e a decomposição por domínio. A primeira organiza o software segundo camadas tecnológicas — como apresentação, lógica de negócio e persistência. A segunda agrupa funcionalidades em torno de conceitos de negócio, como catálogo de produtos, processamento de pedidos ou gestão de pagamentos.
Historicamente, a decomposição técnica foi dominante desde os anos 1970, com arquiteturas em camadas e padrões MVC. A partir dos anos 2000, com a popularização do Domain-Driven Design (DDD) proposto por Eric Evans, a decomposição por domínio ganhou força, especialmente em sistemas com alta complexidade de negócio. Hoje, arquitetos de software precisam entender ambos os paradigmas para tomar decisões contextualizadas.
2. Decomposição por Capacidade Técnica: Estrutura e Características
Na decomposição por capacidade técnica, o sistema é particionado de acordo com funções tecnológicas. Uma arquitetura em camadas típica separa:
- Camada de apresentação: interfaces com o usuário (APIs REST, interfaces web)
- Camada de lógica de negócio: regras e processamentos do domínio
- Camada de dados: acesso a bancos de dados, repositórios
- Camada de infraestrutura: serviços externos, mensageria, logging
Um exemplo de estrutura de projeto seguindo essa abordagem:
src/
controllers/
UserController.java
ProductController.java
OrderController.java
services/
UserService.java
ProductService.java
OrderService.java
repositories/
UserRepository.java
ProductRepository.java
OrderRepository.java
utils/
DateUtils.java
ValidationUtils.java
Essa organização é intuitiva para equipes técnicas e facilita a aplicação de padrões transversais como logging, autenticação e validação. No entanto, quando um requisito de negócio exige alteração em múltiplas camadas — por exemplo, adicionar um campo em formulário, lógica e banco — o desenvolvedor precisa modificar arquivos em controllers, services e repositories, espalhando a mudança por todo o projeto.
3. Decomposição por Domínio: Estrutura e Características
A decomposição por domínio organiza o código em torno de contextos delimitados (bounded contexts) do DDD. Cada domínio contém suas próprias camadas internas, modelos de dados e regras de negócio. Exemplo:
src/
catalogo/
controllers/
ProdutoController.java
services/
ProdutoService.java
repositories/
ProdutoRepository.java
models/
Produto.java
Categoria.java
pedidos/
controllers/
PedidoController.java
services/
PedidoService.java
repositories/
PedidoRepository.java
models/
Pedido.java
ItemPedido.java
pagamentos/
controllers/
PagamentoController.java
services/
PagamentoService.java
repositories/
PagamentoRepository.java
models/
Pagamento.java
Transacao.java
Cada domínio é autossuficiente: alterações no catálogo não afetam pedidos, a menos que haja uma interface explícita entre eles. Isso promove alta coesão funcional e baixo acoplamento entre domínios. A complexidade técnica (como frameworks de persistência) fica encapsulada dentro de cada módulo, permitindo que equipes diferentes evoluam domínios de forma independente.
4. Critérios de Decisão: Quando Escolher Cada Abordagem
A escolha entre decomposição técnica e por domínio depende de múltiplos fatores:
Complexidade do domínio de negócio: Domínios simples com regras estáveis se beneficiam da decomposição técnica, que é mais rápida de implementar. Domínios ricos e mutáveis — como sistemas financeiros, de saúde ou logística — exigem a coesão da decomposição por domínio para isolar mudanças.
Tamanho e maturidade da equipe: Equipes pequenas (até 5 desenvolvedores) podem gerenciar bem a decomposição técnica. Equipes grandes (acima de 10 pessoas) se beneficiam da divisão por domínio, pois cada time pode se especializar em um contexto de negócio, reduzindo conflitos de merge e dependências.
Necessidades de evolução independente: Se o sistema precisa permitir deploys separados por módulo, a decomposição por domínio é praticamente obrigatória. Em monolitos com deploy único, a decomposição técnica ainda é viável, mas exige disciplina para evitar acoplamento excessivo.
5. Impactos na Manutenibilidade e Coesão do Sistema
A coesão de uma decomposição técnica é do tipo tecnológica: componentes do mesmo tipo (todos os controllers) estão juntos. Isso facilita encontrar padrões técnicos, mas dificulta localizar todas as partes de uma funcionalidade de negócio. Por exemplo, para implementar "cancelar pedido", é preciso navegar por OrderController, OrderService, OrderRepository e possivelmente PaymentService.
Na decomposição por domínio, a coesão é funcional: todos os arquivos relacionados a "pedidos" estão no mesmo módulo. Alterar a regra de cancelamento exige modificar apenas arquivos dentro de pedidos/. O acoplamento entre domínios é reduzido por meio de interfaces e eventos, enquanto o acoplamento técnico é gerenciado internamente.
O efeito prático: requisitos transversais (como adicionar autenticação OAuth) são mais fáceis na decomposição técnica, pois afetam uma camada inteira. Requisitos de negócio localizados (como alterar cálculo de frete) são mais fáceis na decomposição por domínio.
6. Estratégias Híbridas e Transição Entre Abordagens
Na prática, muitas arquiteturas combinam os dois paradigmas. Uma abordagem híbrida comum é a decomposição em "fat services" ou módulos: cada domínio possui suas próprias camadas técnicas internas. Exemplo:
src/
shared/
logging/
security/
messaging/
catalogo/
application/
ProdutoUseCase.java
domain/
Produto.java
infrastructure/
ProdutoRepositoryImpl.java
pedidos/
application/
PedidoUseCase.java
domain/
Pedido.java
infrastructure/
PedidoRepositoryImpl.java
A camada application (use cases) atua como ponto de conexão entre a lógica técnica (infraestrutura) e a lógica de domínio. Essa estrutura permite que cada domínio evolua independentemente, enquanto componentes compartilhados (como logging e segurança) são reutilizados.
Para migrar de uma arquitetura em camadas para módulos por domínio, recomenda-se a extração incremental: identificar um domínio coeso, movê-lo para um módulo separado com suas próprias camadas internas, e repetir o processo para cada contexto delimitado. Ferramentas como análise de dependências (ex.: JDepend, ArchUnit) ajudam a identificar componentes que podem ser isolados.
7. Conclusão e Recomendações Práticas
A decomposição por capacidade técnica oferece simplicidade inicial e facilidade para aplicar padrões transversais, mas tende a gerar sistemas frágeis quando o domínio de negócio é complexo. A decomposição por domínio promove alta coesão funcional e evolução independente, porém exige mais planejamento inicial e maturidade da equipe.
Checklist para escolha da decomposição adequada:
- Domínio simples e estável? → Decomposição técnica
- Domínio rico e mutável? → Decomposição por domínio
- Equipe pequena (≤5)? → Ambas são viáveis; priorize técnica se o domínio for simples
- Equipe grande (>10)? → Decomposição por domínio para reduzir dependências
- Necessidade de deploys independentes? → Decomposição por domínio
- Sistema legado em camadas? → Migre incrementalmente para módulos por domínio
Para aprofundamento, recomenda-se estudar Domain-Driven Design (Eric Evans), Clean Architecture (Robert C. Martin) e padrões de modularização como o Modular Monolith.
Referências
- Domain-Driven Design: Tackling Complexity in the Heart of Software — Livro de Eric Evans que introduziu os conceitos de bounded contexts e decomposição por domínio.
- Clean Architecture: A Craftsman's Guide to Software Structure and Design — Robert C. Martin apresenta princípios de separação por domínio e camadas.
- Modular Monolith: A Primer — Artigo técnico detalhando como estruturar monolitos modulares com decomposição por domínio.
- ArchUnit: A Java Library for Testing Architecture — Ferramenta prática para verificar regras de decomposição e dependências entre módulos.
- Bounded Context — Martin Fowler explica o conceito central do DDD para delimitar domínios e evitar acoplamento indevido.