.NET Clean Architecture: Código Limpo e Testável na Prática

Introdução à Clean Architecture no .NET

A Clean Architecture, proposta por Robert C. Martin (Uncle Bob), é uma filosofia de design de software que visa criar sistemas altamente testáveis, manuteníveis e independentes de frameworks, bancos de dados e UI. No contexto do .NET, a aplicação dos princípios da Clean Architecture promove a separação de responsabilidades, o que resulta em um código mais limpo e flexível.

O objetivo principal é isolar a lógica de negócios do seu sistema das preocupações de infraestrutura, como frameworks específicos, ORMs e bancos de dados. Isso permite que você altere essas partes do seu sistema sem impactar a lógica central, facilitando a adaptação a novas tecnologias e requisitos.

Camadas da Clean Architecture

A Clean Architecture é organizada em camadas concêntricas, com o código mais interno sendo o mais abstrato e o código mais externo sendo o mais concreto. As camadas dependem apenas das camadas mais internas. Isso garante que as mudanças em uma camada externa não afetem as camadas internas. As principais camadas são:

  • Entities: Representam as regras de negócio mais críticas. São as entidades do domínio e não dependem de nada externo.
  • Use Cases: Contêm a lógica de negócios específica da aplicação. Coordenam as entidades para realizar tarefas específicas.
  • Interface Adapters: Converte os dados do formato mais conveniente para os Use Cases e vice-versa. Inclui presenters, gateways e controllers.
  • Frameworks and Drivers: A camada mais externa. Contém frameworks, bancos de dados, UI e outros detalhes de implementação.

A regra de dependência fundamental é: as dependências só podem apontar para dentro. Nada na camada interna pode depender de nada na camada externa. Isso é crucial para a testabilidade e manutenção.

Implementando a Clean Architecture no .NET: Um Exemplo Prático

Vamos considerar um cenário simples: um sistema de gerenciamento de tarefas (To-Do List). Mostraremos como podemos estruturar o código .NET usando os princípios da Clean Architecture.

1. Entities

Nesta camada, definimos a entidade Task (Tarefa). Ela contém as regras de negócio básicas para uma tarefa.


// Entities/Task.cs
public class Task
{
public Guid Id { get; private set; }
public string Title { get; private set; }
public string Description { get; private set; }
public bool IsCompleted { get; private set; }
public Task(string title, string description)
{
Id = Guid.NewGuid();
Title = title;
Description = description;
IsCompleted = false;
}
public void Complete()
{
IsCompleted = true;
}
}

2. Use Cases

Aqui definimos os casos de uso, como criar uma tarefa, completar uma tarefa e listar tarefas. Usamos interfaces para desacoplar a implementação dos casos de uso.


// UseCases/CreateTask/ICreateTaskUseCase.cs
public interface ICreateTaskUseCase
{
Task Execute(string title, string description);
}
// UseCases/CompleteTask/ICompleteTaskUseCase.cs
public interface ICompleteTaskUseCase
{
void Execute(Guid taskId);
}
// UseCases/ListTasks/IListTasksUseCase.cs
public interface IListTasksUseCase
{
IEnumerable<Task> Execute();
}

E a implementação dos casos de uso:


// UseCases/CreateTask/CreateTaskUseCase.cs
public class CreateTaskUseCase : ICreateTaskUseCase
{
private readonly ITaskRepository _taskRepository;
public CreateTaskUseCase(ITaskRepository taskRepository)
{
_taskRepository = taskRepository;
}
public Task Execute(string title, string description)
{
var task = new Task(title, description);
_taskRepository.Add(task);
return task;
}
}
// UseCases/CompleteTask/CompleteTaskUseCase.cs
public class CompleteTaskUseCase : ICompleteTaskUseCase
{
private readonly ITaskRepository _taskRepository;
public CompleteTaskUseCase(ITaskRepository taskRepository)
{
_taskRepository = taskRepository;
}
public void Execute(Guid taskId)
{
var task = _taskRepository.GetById(taskId);
if (task != null)
{
task.Complete();
_taskRepository.Update(task);
}
}
}
// UseCases/ListTasks/ListTasksUseCase.cs
public class ListTasksUseCase : IListTasksUseCase
{
private readonly ITaskRepository _taskRepository;
public ListTasksUseCase(ITaskRepository taskRepository)
{
_taskRepository = taskRepository;
}
public IEnumerable<Task> Execute()
{
return _taskRepository.GetAll();
}
}

3. Interface Adapters

Nesta camada, definimos os adaptadores, incluindo o repositório (ITaskRepository) e os controllers da API (se aplicável). O repositório é uma interface que abstrai o acesso aos dados.


// InterfaceAdapters/ITaskRepository.cs
public interface ITaskRepository
{
Task GetById(Guid id);
IEnumerable<Task> GetAll();
void Add(Task task);
void Update(Task task);
void Delete(Guid id);
}

Se estivéssemos usando uma API, os controllers estariam aqui, convertendo os dados da requisição para o formato esperado pelos Use Cases e convertendo os resultados dos Use Cases para o formato da resposta da API.

4. Frameworks and Drivers

Esta é a camada mais externa. Aqui implementamos o repositório (por exemplo, usando Entity Framework Core ou uma lista em memória para testes) e os controllers da API (usando ASP.NET Core, por exemplo).


// FrameworksAndDrivers/TaskRepository.cs (Exemplo usando uma lista em memória)
public class TaskRepository : ITaskRepository
{
private readonly List<Task> _tasks = new List<Task>();
public Task GetById(Guid id)
{
return _tasks.FirstOrDefault(t => t.Id == id);
}
public IEnumerable<Task> GetAll()
{
return _tasks;
}
public void Add(Task task)
{
_tasks.Add(task);
}
public void Update(Task task)
{
// Lógica para atualizar a tarefa na lista
}
public void Delete(Guid id)
{
// Lógica para remover a tarefa da lista
}
}

Testabilidade

Um dos grandes benefícios da Clean Architecture é a facilidade de testar o código. Como cada camada é independente das outras, podemos escrever testes unitários para cada camada sem precisar se preocupar com dependências externas.

Por exemplo, podemos testar os Use Cases facilmente, injetando mocks do repositório:


// Exemplo de teste unitário para CreateTaskUseCase
[Fact]
public void CreateTaskUseCase_Execute_CreatesNewTask()
{
// Arrange
var mockTaskRepository = new Mock<ITaskRepository>();
var createTaskUseCase = new CreateTaskUseCase(mockTaskRepository.Object);
// Act
var task = createTaskUseCase.Execute("Test Task", "Test Description");
// Assert
Assert.NotNull(task);
mockTaskRepository.Verify(repo => repo.Add(task), Times.Once);
}

Benefícios da Clean Architecture

  • Testabilidade: Facilidade de escrever testes unitários e de integração.
  • Manutenibilidade: Facilidade de modificar e estender o código.
  • Flexibilidade: Facilidade de adaptar o sistema a novas tecnologias e requisitos.
  • Independência: Independência de frameworks, bancos de dados e UI.
  • Reusabilidade: Possibilidade de reutilizar componentes em diferentes partes do sistema.

Considerações

A Clean Architecture pode adicionar complexidade inicial ao projeto, principalmente em projetos menores. É importante avaliar se os benefícios superam os custos antes de adotá-la. Em projetos maiores e mais complexos, os benefícios da Clean Architecture geralmente superam os custos iniciais.

Comece pequeno, implementando a Clean Architecture em partes do seu sistema e, gradualmente, estendendo-a para outras partes. Não tente implementar a Clean Architecture perfeitamente desde o início. É um processo iterativo de aprendizado e refinamento.

Conclusão

A Clean Architecture é uma abordagem poderosa para projetar sistemas .NET que são fáceis de testar, manter e evoluir. Ao separar a lógica de negócios das preocupações de infraestrutura, você cria um código mais flexível e adaptável. Embora possa adicionar complexidade inicial, os benefícios a longo prazo em termos de testabilidade, manutenibilidade e flexibilidade geralmente superam os custos. Ao adotar os princípios da Clean Architecture, você estará construindo sistemas .NET mais robustos e sustentáveis.

Perguntas Frequentes (FAQs)

O que é Clean Architecture?

Clean Architecture é uma filosofia de design de software que visa criar sistemas altamente testáveis, manuteníveis e independentes de frameworks, bancos de dados e UI.

Quais são as principais camadas da Clean Architecture?

As principais camadas são: Entities, Use Cases, Interface Adapters e Frameworks and Drivers.

Qual é a regra de dependência na Clean Architecture?

As dependências só podem apontar para dentro. Nada na camada interna pode depender de nada na camada externa.

Quais são os benefícios da Clean Architecture?

Os benefícios incluem testabilidade, manutenibilidade, flexibilidade, independência e reusabilidade.

A Clean Architecture é adequada para todos os projetos?

Não necessariamente. Em projetos menores, a complexidade inicial pode não valer a pena. É importante avaliar se os benefícios superam os custos.

Como posso começar a implementar a Clean Architecture no meu projeto .NET?

Comece pequeno, implementando a Clean Architecture em partes do seu sistema e, gradualmente, estendendo-a para outras partes. Não tente implementar a Clean Architecture perfeitamente desde o início. É um processo iterativo de aprendizado e refinamento.

Qual a diferença entre Clean Architecture e Onion Architecture?

Clean Architecture e Onion Architecture são conceitos muito similares e frequentemente usados de forma intercambiável. A principal diferença, se houver, reside na ênfase e na nomenclatura. Ambos buscam o mesmo objetivo: desacoplar a lógica de negócios da infraestrutura.

Como lidar com a persistência de dados (banco de dados) na Clean Architecture?

A persistência de dados (banco de dados) é tratada na camada mais externa (Frameworks and Drivers). A camada de Interface Adapters define interfaces (como `ITaskRepository` no exemplo) que abstraem o acesso aos dados. A implementação concreta do repositório, usando Entity Framework Core ou outro ORM, fica na camada de Frameworks and Drivers.

Como a Clean Architecture se relaciona com Domain-Driven Design (DDD)?

A Clean Architecture e o DDD são complementares. A Clean Architecture fornece uma estrutura para organizar o código, enquanto o DDD fornece uma abordagem para modelar o domínio do problema. As Entities na Clean Architecture frequentemente correspondem às entidades do domínio no DDD. A Clean Architecture facilita a implementação dos princípios do DDD, como a separação do domínio da infraestrutura.

Deixe um comentário