Migrar de uma arquitetura de 3 camadas para uma arquitetura limpa pode parecer desafiador, mas é um passo importante para melhorar a manutenibilidade, escalabilidade e flexibilidade de um projeto. Enquanto a arquitetura de 3 camadas separa as preocupações de apresentação, lógica de negócios e acesso a dados, a Arquitetura Limpa (ou Clean Architecture) busca uma maior separação de responsabilidades, enfatizando a inversão de dependência, de forma que a lógica central da aplicação (Domínio) não dependa de implementações de infraestrutura.
Neste artigo, exploraremos como realizar essa migração, focando em um exemplo C#/.NET, embora o conceito também possa ser aplicado a outras tecnologias.
Arquitetura de 3 Camadas vs Arquitetura Limpa
Antes de iniciarmos a migração, é importante entender a diferença entre as duas arquiteturas.
-
Arquitetura de 3 camadas: Normalmente consiste em uma Camada de Apresentação (UI), uma Camada de Negócio (lógica de aplicação) e uma Camada de Acesso a Dados. Aqui, a camada de negócio frequentemente se conecta diretamente à camada de dados.
-
Arquitetura Limpa: Baseada no princípio da inversão de dependência, ela separa mais claramente as preocupações em quatro grandes camadas: Domínio, Aplicação, Infraestrutura e Interface de Usuário. A regra de ouro da Arquitetura Limpa é que as dependências devem sempre apontar para o centro, ou seja, as camadas externas dependem das internas, mas nunca o contrário.
A transição entre essas duas arquiteturas pode melhorar o design da aplicação, facilitando a testabilidade e a manutenção.
Configurando os Projetos para Arquitetura Limpa
Para começar, você precisará reorganizar seus projetos de forma que reflitam a separação de responsabilidades da Arquitetura Limpa. Siga estas etapas:
1. Renomeie sua Camada de Acesso a Dados para Camada de Domínio
A Camada de Domínio contém as entidades, regras de negócio e lógica de domínio fundamentais da aplicação. Na Arquitetura de 3 Camadas, a lógica de negócio pode estar misturada à camada de acesso a dados. Na Arquitetura Limpa, ela é movida para o Domínio, com foco em manter a lógica de negócios independente de qualquer infraestrutura ou tecnologia específica.
2. Renomeie sua Camada de Negócio para Camada de Aplicação
A Camada de Aplicação lida com casos de uso específicos e coordena o comportamento do sistema. Ela define interfaces para repositórios e serviços que serão implementados em outras camadas (como a Infraestrutura), mas não contém implementações concretas. Isso permite que a camada de aplicação seja totalmente testável e independente da infraestrutura.
3. Adicione a Camada de Infraestrutura
A Camada de Infraestrutura contém as implementações concretas dos repositórios, serviços de terceiros, contexto de banco de dados, entre outros. Esta camada é onde sua aplicação se conecta ao mundo externo (banco de dados, APIs, etc.), mas essas implementações devem ser abstraídas por interfaces que estão definidas nas camadas mais internas, como o Domínio ou a Aplicação.
4. Atualize as Referências
Na Arquitetura Limpa, as referências entre as camadas devem sempre seguir a regra de dependências apontando para o centro. Uma referência típica seria:
- Apresentação (UI) → Aplicação → Domínio
- Infraestrutura → Aplicação
Ou seja, a UI depende da aplicação, que por sua vez depende do domínio. A infraestrutura, que implementa detalhes como o acesso ao banco de dados, também depende da aplicação, mas a aplicação nunca deve depender diretamente da infraestrutura.
Movendo o Contexto do Banco de Dados
Na arquitetura de 3 camadas, o contexto de banco de dados (ex. DbContext
no Entity Framework) costuma estar na camada de acesso a dados. Em uma Arquitetura Limpa, o contexto de banco de dados deve ser movido para a Camada de Infraestrutura, pois trata-se de um detalhe de implementação. A lógica de negócios e as regras associadas ao comportamento do domínio permanecem na Camada de Domínio.
-
Infraestrutura: Implementa o
DbContext
, repositórios e quaisquer interações diretas com o banco de dados. - Domínio: Contém as entidades e objetos de valor (sem dependências de infraestrutura).
Implementando o Repositório
Na Camada de Aplicação, você define interfaces para os repositórios. Por exemplo, pode-se definir uma interface IProductRepository
na camada de aplicação:
public interface IProductRepository
{
Task<Product> GetByIdAsync(int id);
Task<IEnumerable<Product>> GetAllAsync();
// Outros métodos...
}
A implementação concreta desta interface será feita na Camada de Infraestrutura. A inversão de dependência garante que a aplicação possa funcionar com qualquer implementação de repositório, tornando mais fácil mudar de um banco de dados SQL para NoSQL, por exemplo, sem alterar o restante do código.
public class ProductRepository : IProductRepository
{
private readonly ApplicationDbContext _context;
public ProductRepository(ApplicationDbContext context)
{
_context = context;
}
public async Task<Product> GetByIdAsync(int id)
{
return await _context.Products.FindAsync(id);
}
public async Task<IEnumerable<Product>> GetAllAsync()
{
return await _context.Products.ToListAsync();
}
}
Consumindo o Repositório na Camada de Aplicação
Uma vez que o repositório foi implementado na camada de infraestrutura, você pode consumi-lo na camada de aplicação por meio da injeção de dependência. A Camada de Aplicação deve depender apenas de interfaces, não de implementações concretas.
Exemplo de um caso de uso que consome o repositório:
public class GetProductByIdQueryHandler
{
private readonly IProductRepository _productRepository;
public GetProductByIdQueryHandler(IProductRepository productRepository)
{
_productRepository = productRepository;
}
public async Task<ProductDto> Handle(int productId)
{
var product = await _productRepository.GetByIdAsync(productId);
return new ProductDto(product);
}
}
Atualizando Nomes e Referências
Durante a migração, é crucial atualizar os nomes dos arquivos, namespaces e referências de projetos para seguir a nova organização. Isso inclui:
- Renomear Data Access Layer (DAL) para Infraestrutura.
- Atualizar a camada de negócio para ser a Aplicação, que define interfaces e casos de uso.
- Verificar se o registro de serviços no Startup.cs (ou Program.cs) está correto, especialmente para a injeção de dependência entre as camadas.
Lembre-se de recompilar o projeto após cada modificação significativa para garantir que não haja erros ou referências quebradas.
Conclusão
Migrar de uma arquitetura de 3 camadas para uma Arquitetura Limpa pode parecer uma tarefa desafiadora no início, mas o processo pode ser simplificado ao seguir os princípios de inversão de dependência e separação clara de responsabilidades. A reorganização das camadas melhora a manutenibilidade do sistema, permitindo uma maior flexibilidade ao adicionar ou modificar funcionalidades no futuro.