Arquitetura em camadas: presentation, business e data
1. Introdução à Arquitetura em Camadas
A arquitetura em camadas é um dos padrões mais antigos e amplamente adotados no desenvolvimento de software. Seu princípio fundamental é organizar o sistema em níveis hierárquicos, onde cada camada possui responsabilidades bem definidas e se comunica apenas com as camadas adjacentes. O objetivo central é separar preocupações (separation of concerns), promovendo coesão interna e baixo acoplamento entre os componentes.
Historicamente, esse modelo ganhou força com a migração dos sistemas mainframe para arquiteturas cliente-servidor nos anos 1990, e se consolidou com o surgimento de frameworks como J2EE e .NET. A popularização da internet e das aplicações web reforçou a necessidade de separar claramente a interface do usuário (presentation), a lógica de negócio (business) e o acesso a dados (data).
Os benefícios dessa separação são múltiplos:
- Coesão: cada camada agrupa funcionalidades relacionadas.
- Baixo acoplamento: alterações em uma camada impactam minimamente as demais.
- Reutilização: a lógica de negócio pode ser reutilizada por diferentes interfaces (web, mobile, API).
- Testabilidade: é possível testar cada camada de forma isolada.
2. A Camada de Apresentação (Presentation Layer)
A camada de apresentação é responsável por toda a interação com o usuário. Ela gerencia a exibição de informações e a captura de entradas, delegando o processamento para as camadas inferiores. Suas principais responsabilidades incluem:
- Renderização de interfaces (telas, páginas, formulários).
- Validação básica de entrada (formato de campos, obrigatoriedade).
- Orquestração de navegação e estados da interface.
- Tratamento de erros de usuário (mensagens amigáveis).
Padrões comuns associados a essa camada incluem:
- MVC (Model-View-Controller): separa a interface em modelo, visão e controlador.
- MVP (Model-View-Presenter): o presenter contém a lógica de apresentação.
- MVVM (Model-View-ViewModel): popular em frameworks reativos como Angular e WPF.
Exemplo de um controlador MVC em uma aplicação web:
// Presentation Layer - Controller
public class UsuarioController {
private UsuarioService usuarioService;
public UsuarioController(UsuarioService service) {
this.usuarioService = service;
}
public ViewModel obterUsuario(int id) {
UsuarioDTO dto = usuarioService.buscarPorId(id);
return new ViewModel(dto.nome, dto.email);
}
}
O que NÃO deve pertencer à apresentação:
- Regras de negócio (ex.: cálculo de imposto, validação de elegibilidade).
- Acesso direto a banco de dados ou APIs externas.
- Lógica de persistência ou cache de dados.
3. A Camada de Negócio (Business Layer)
Esta é a camada central da aplicação, onde residem as regras de negócio, validações complexas e fluxos de domínio. Ela deve ser independente de frameworks e tecnologias de interface ou persistência, garantindo que a lógica fundamental do sistema possa ser testada e evoluída sem amarras técnicas.
Componentes típicos da camada de negócio:
- Entidades de domínio: objetos que representam conceitos do negócio (ex.: Pedido, Cliente, Produto).
- Serviços de aplicação: orquestram casos de uso, coordenando entidades e serviços de domínio.
- Serviços de domínio: encapsulam regras que não pertencem a uma única entidade.
Exemplo de serviço de aplicação:
// Business Layer - Application Service
public class PedidoService {
private PedidoRepository pedidoRepository;
private EstoqueService estoqueService;
public PedidoService(PedidoRepository repo, EstoqueService estoque) {
this.pedidoRepository = repo;
this.estoqueService = estoque;
}
public void criarPedido(List<ItemPedido> itens, Cliente cliente) {
// Regra de negócio: verificar estoque antes de criar o pedido
for (ItemPedido item : itens) {
if (!estoqueService.temDisponibilidade(item.produto, item.quantidade)) {
throw new EstoqueInsuficienteException(item.produto);
}
}
Pedido pedido = new Pedido(cliente, itens);
pedidoRepository.salvar(pedido);
}
}
Para manter a testabilidade, a camada de negócio deve:
- Depender de abstrações (interfaces), não de implementações concretas.
- Não conter referências a bibliotecas de interface gráfica ou ORMs.
- Permitir injeção de dependências para facilitar mocks em testes unitários.
4. A Camada de Dados (Data Layer)
A camada de dados é responsável por toda a comunicação com fontes de armazenamento: bancos relacionais, NoSQL, sistemas de arquivos, APIs externas, etc. Seu objetivo é abstrair os detalhes de persistência para as camadas superiores, oferecendo uma interface limpa e consistente.
Padrões comuns:
- Repository: abstrai o acesso a dados, simulando uma coleção em memória.
- DAO (Data Access Object): encapsula operações CRUD específicas.
- Unit of Work: gerencia transações e rastreia alterações.
Exemplo de um repositório:
// Data Layer - Repository Interface
public interface PedidoRepository {
Pedido buscarPorId(int id);
void salvar(Pedido pedido);
List<Pedido> buscarPorCliente(int clienteId);
}
// Data Layer - Concrete Implementation
public class PedidoRepositoryJPA implements PedidoRepository {
private EntityManager entityManager;
public Pedido buscarPorId(int id) {
return entityManager.find(Pedido.class, id);
}
public void salvar(Pedido pedido) {
entityManager.persist(pedido);
}
public List<Pedido> buscarPorCliente(int clienteId) {
return entityManager.createQuery(
"SELECT p FROM Pedido p WHERE p.cliente.id = :clienteId", Pedido.class)
.setParameter("clienteId", clienteId)
.getResultList();
}
}
O Princípio da Inversão de Dependência (DIP) é essencial aqui: as camadas superiores definem as interfaces que a camada de dados implementa. Isso permite trocar a tecnologia de persistência (ex.: de JPA para JDBC ou MongoDB) sem afetar a lógica de negócio.
5. Regras de Dependência e Fluxo de Comunicação
A regra fundamental da arquitetura em camadas é que camadas superiores dependem de camadas inferiores, nunca o contrário. O fluxo típico de comunicação é descendente:
- Apresentação → chama serviços da camada de negócio.
- Negócio → chama repositórios da camada de dados.
- Dados → interage com o banco ou API externa.
A comunicação ascendente (da camada inferior para a superior) deve ser feita por meio de eventos ou callbacks, evitando dependências diretas. Por exemplo, a camada de dados pode notificar a camada de negócio sobre alterações via eventos de domínio.
Violações comuns:
- Vazar entidades de dados (ex.: objetos JPA) para a camada de apresentação, expondo detalhes de implementação.
- Colocar lógica de negócio em controllers ou repositórios.
- Criar dependências circulares entre camadas (ex.: negócio chamando apresentação).
6. Comparação com Arquiteturas Contemporâneas
A arquitetura em camadas clássica serviu de base para padrões mais modernos:
- Clean Architecture (Robert C. Martin): mantém a separação em camadas, mas inverte a direção das dependências. O domínio fica no centro, e as camadas externas (interface, infraestrutura) dependem dele. É uma evolução que reforça o DIP.
- Arquitetura Hexagonal (Ports and Adapters): similar à Clean Architecture, define portas (interfaces) no núcleo e adaptadores (implementações) na periferia. A comunicação é bidirecional através de portas.
A abordagem clássica ainda é a melhor escolha quando:
- O sistema é relativamente simples e não justifica a complexidade adicional.
- A equipe está familiarizada com o modelo e os prazos são curtos.
- Não há necessidade de suportar múltiplas interfaces ou fontes de dados heterogêneas.
7. Boas Práticas e Armadilhas Comuns
Organização de pacotes:
- Por camada: controller/, service/, repository/. Simples, mas pode gerar muitos arquivos.
- Por funcionalidade: pedido/, cliente/, produto/. Melhor coesão, mas requer cuidado com dependências.
Armadilhas:
- Camadas anêmicas: quando a camada de negócio se torna apenas um pass-through para a camada de dados.
- Dependências circulares: geralmente resolvidas com inversão de dependência ou introdução de uma nova abstração.
- Vazamento de abstrações: expor detalhes de ORM (ex.: @Entity, @Column) para a apresentação.
Estratégias de evolução:
- Comece com uma arquitetura simples em três camadas.
- Refatore incrementalmente: introduza interfaces, depois serviços de domínio, e eventualmente eventos.
- Adote padrões como CQRS (Command Query Responsibility Segregation) se a complexidade crescer.
Referências
- Martin Fowler - Patterns of Enterprise Application Architecture — Livro clássico que define os padrões de camadas, incluindo Service Layer, Repository e Data Mapper.
- Microsoft - N-tier architecture style — Guia oficial da Microsoft sobre arquitetura em camadas (N-tier) com exemplos práticos em .NET.
- Robert C. Martin - The Clean Architecture — Artigo seminal que apresenta a Clean Architecture como evolução da arquitetura em camadas tradicional.
- Alistair Cockburn - Hexagonal Architecture — Descrição original da Arquitetura Hexagonal (Ports and Adapters) e sua relação com camadas.
- Oracle - The Java EE 6 Tutorial: Layered Architecture — Tutorial oficial da Oracle sobre arquitetura em camadas no contexto Java EE, abordando presentation, business e data tiers.
- Baeldung - Clean Architecture with Spring — Tutorial prático que implementa Clean Architecture em Spring Boot, comparando com a abordagem clássica em camadas.