Como não se afogar no espaguete: Arquitetura Hexagonal em C#

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Quando o projeto vira uma bagunça

Você já abriu um projeto C# escrito há um ano e percebeu com horror que nada pode ser alterado sem o risco de quebrar tudo? A lógica de negócios está fortemente acoplada ao Entity Framework, os controladores ASP.NET guardam segredos do banco de dados, e os testes unitários se transformam em uma integração infernal com o banco real. Familiar?

Este é o sintoma clássico da falta de arquitetura. A boa notícia: existe uma receita comprovada — a Arquitetura Hexagonal (também conhecida como Ports & Adapters). Este padrão, criado por Alistair Cockburn, não é apenas um termo da moda. É uma salvação para projetos de médio e grande porte em C#. Vamos entender como funciona e já escrever código.



A essência da Arquitetura Hexagonal: núcleo e periferia

Imagine um hexágono. No centro está sua aplicação com lógica de negócios pura, escrita em C# puro, sem dependências externas. Nas bordas estão as portas (interfaces), através das quais o núcleo se comunica com o mundo externo. Os adaptadores são as implementações concretas: controladores ASP.NET, repositórios EF Core, clientes RabbitMQ.

A regra principal: o núcleo não sabe nada sobre o mundo externo. Ele conhece apenas suas portas. Isso permite trocar o banco de dados, a interface do usuário ou as filas de mensagens sem alterar a lógica de negócios.



Por que isso é importante para o desenvolvedor C#?

  • Testabilidade: A lógica de negócios pode ser testada sem banco de dados, HTTP ou sistema de arquivos.
  • Flexibilidade: Quer trocar PostgreSQL por MongoDB? Basta alterar o adaptador.
  • Legibilidade: O código se torna semelhante a uma especificação de requisitos, e não a um emaranhado de dependências.
  • Conformidade com SOLID: Especialmente o princípio da inversão de dependência (Dependency Inversion).


Estrutura do projeto em C#

Na Arquitetura Hexagonal, geralmente distinguimos três camadas principais. Para C#, é conveniente organizá-las como projetos separados em uma mesma solução:



MyApp.sln├── MyApp.Domain          // Núcleo: entidades, value objects, portas (interfaces)├── MyApp.Application     // Casos de uso (serviços da aplicação)├── MyApp.Infrastructure  // Adaptadores: EF Core, APIs externas, logging└── MyApp.Api             // Adaptador de entrada: ASP.NET Controllers / Minimal API


Observe: Domain e Application são bibliotecas de classes puras, sem referências a Microsoft.AspNetCore ou EntityFramework. Apenas System e System.Threading.Tasks.



Escrevendo código: exemplo real

Suponha que estamos criando um sistema de pedidos. Precisamos criar um pedido e salvá-lo.



Passo 1. Núcleo (Domain)

Aqui vivem as entidades e as portas — interfaces que definem como o núcleo se comunica com o mundo.



// MyApp.Domain/Entities/Order.cspublic class Order{    public Guid Id { get; private set; }    public string CustomerName { get; private set; }    public decimal TotalAmount { get; private set; }    public DateTime CreatedAt { get; private set; }

public Order(string customerName, decimal totalAmount) { Id = Guid.NewGuid(); CustomerName = customerName; TotalAmount = totalAmount; CreatedAt = DateTime.UtcNow; }}

// MyApp.Domain/Ports/IOrderRepository.cs (Porta de saída)public interface IOrderRepository{ Task<Order> GetByIdAsync(Guid id); Task AddAsync(Order order);}

// MyApp.Domain/Ports/IOrderNotifier.cs (Porta de saída — notificações)public interface IOrderNotifier{ Task NotifyOrderCreatedAsync(Order order);}


Observe: IOrderRepository e IOrderNotifier são as portas. O núcleo define o que ele quer, mas não como isso é implementado.



Passo 2. Casos de uso

Blogs

Book Recommendations