Tag: architettura, hexagonal-architecture, ports-and-adapters, ddd, backend, clean-architecture
Livello: intermedio
TL;DR: Disaccoppia il dominio applicativo da framework e infrastruttura tramite porte e adattatori, rendendo la logica di business pura, testabile e intercambiabile.
Il core contiene le entita' e la logica di business pura, senza dipendenze esterne.
La regola di dipendenza punta sempre verso l'interno: l'infrastruttura dipende dal core.
Una porta e' un'interfaccia che definisce il contratto di interazione con il dominio.
Le porte primarie o driving espongono i casi d'uso verso l'esterno.
Le porte secondarie o driven definiscono i servizi infrastrutturali richiesti dal core.
Gli adattatori primari convertono richieste esterne in chiamate al dominio applicativo.
Gli adattatori secondari implementano le porte secondarie verso database, code e servizi terzi.
L'inversione delle dipendenze garantisce l'isolamento completo del dominio tecnologico.
Permette di testare l'intera logica di business sostituendo l'infrastruttura con mock o fake.
La forma a esagono rappresenta visivamente molteplici canali di interazione simmetrici.
Q: Qual e' la differenza concettuale tra porta e adattatore?R: La porta e' l'interfaccia astratta definita dal dominio per stabilire un contratto, mentre l'adattatore e' l'implementazione concreta che dialoga con la tecnologia specifica.
Q: Come si distinguono driving ports e driven ports durante l'esecuzione?R: Le driving port guidano l'applicazione e sono invocate dai controller esterni, mentre le driven port sono chiamate dal dominio verso risorse esterne come database o broker.
Q: Come gestisci le transazioni su database senza violare l'isolamento del dominio?R: Incapsulo la transazione in un middleware dell'adattatore o uso un'interfaccia UnitOfWork dichiarata nel dominio e implementata nell'infrastruttura.
Far dipendere le entita' di dominio da annotazioni ORM o librerie di terze parti.
Far trapelare DTO HTTP o modelli di persistenza all'interno della business logic.
Dichiarare le interfacce delle porte nel layer infrastrutturale anziche' nel core.
Applicare l'architettura esagonale a semplici operazioni CRUD aggiungendo complessita' ingiustificata.
Confondere il controller HTTP con la driving port invece che trattarlo come adattatore primario.
// Porta secondaria (definita nel dominio)
export interface OrderRepositoryPort {
save(order: Order): Promise<void>;
findById(id: string): Promise<Order | null>;
}
// Porta primaria / Caso d'uso (nel core applicativo)
export interface CreateOrderUseCase {
execute(command: CreateOrderCommand): Promise<string>;
}
// Adattatore secondario (infrastruttura)
export class PostgresOrderAdapter implements OrderRepositoryPort {
constructor(private readonly db: DatabaseClient) {}
async save(order: Order): Promise<void> {
await this.db.query("INSERT INTO orders ...", [order.id, order.total]);
}
async findById(id: string): Promise<Order | null> {
const record = await this.db.query("SELECT * FROM orders WHERE id = $1", [id]);
return record ? OrderMapper.toDomain(record) : null;
}
}
[ ] Mappatura bidirezionale esplicita tra modelli di dominio e modelli di persistenza
[ ] Pattern Unit of Work per la gestione delle transazioni atomiche
[x] Differenza architetturale tra porte primarie (driving) e secondarie (driven)
[x] Applicazione del Dependency Inversion Principle alle porte esterne
Alistair Cockburn — Hexagonal Architecture (Ports and Adapters, 2005)
Robert C. Martin — Clean Architecture: A Craftsman's Guide to Software Structure and Design
Eric Evans — Domain-Driven Design: Tackling Complexity in the Heart of Software