Clean Architecture e Arquitetura Hexagonal Explicadas
Entenda por que o seu Domínio (Regra de Negócios) deve estar no centro de tudo, totalmente isolado de frameworks, bibliotecas e banco de dados.
Antigamente, as aplicações nasciam ao redor do Banco de Dados. Você criava as tabelas no MySQL, gerava os modelos e a aplicação era apenas uma 'casca' por cima disso. Se você precisasse trocar de banco (de MySQL para MongoDB), você precisaria reescrever a aplicação inteira.
Tanto a Clean Architecture (Robert C. Martin) quanto a Arquitetura Hexagonal (Alistair Cockburn) partem de um princípio oposto: o Banco de Dados, a Interface de Usuário e os Frameworks (como o Express) são apenas Detalhes.
O Domínio no Centro
No centro do sistema deve estar o seu Domínio (suas Entidades e Casos de Uso/Services). O centro não deve importar nenhuma biblioteca externa.
// Repare: Não há imports do TypeORM, do Express, ou do AWS SDK.
// Apenas Typescript puro descrevendo a regra de negócio.
export class Order {
id: string;
total: number;
status: 'PENDING' | 'PAID' | 'CANCELED';
constructor(total: number) {
this.total = total;
this.status = 'PENDING';
}
public pay(): void {
if (this.status !== 'PENDING') throw new Error('Invalid status');
this.status = 'PAID';
}
}Portas e Adaptadores (Ports and Adapters)
A Arquitetura Hexagonal também é conhecida como Ports and Adapters. O seu núcleo de negócio declara Portas (Interfaces), dizendo: 'Eu preciso de algo que salve dados no banco'.
E as bordas do sistema criam Adaptadores (Implementações), dizendo: 'O TypeORM sabe salvar dados, vou implementar essa sua interface'.
// A PORTA (Fica no centro, dita as regras)
import type { Order } from '../entities/Order';
export interface IOrdersRepository {
save(order: Order): Promise<void>;
findById(id: string): Promise<Order | null>;
}// O ADAPTADOR (Fica na borda externa, obedece as regras da Porta)
import type { IOrdersRepository } from '../IOrdersRepository';
import { getRepository } from 'typeorm';
export class TypeORMOrdersRepository implements IOrdersRepository {
private ormRepo = getRepository(OrderModel);
async save(order: Order): Promise<void> {
await this.ormRepo.save(order);
}
// ...
}A Regra de Dependência é estrita: A camada externa (Adaptadores, Frameworks) conhece a camada interna (Interfaces, Entidades). Mas a camada interna JAMAIS conhece a camada externa.
Conclusão
Apesar de exigir mais arquivos (Interfaces, Injeção de Dependências), uma arquitetura limpa blinda sua aplicação. Se o Express for descontinuado amanhã, você só precisa criar um novo Adaptador (Fastify, por exemplo). Suas regras de negócio (Services) ficam intocadas.