Hexagonal architecture ala Alistair Cockburn: domain berdiri di pusat yang bebas framework, driving port untuk aktor eksternal dan driven port untuk infrastruktur didefinisikan sebagai interface milik domain, lalu adapter apa pun — HTTP controller Prisma SMTP hingga fake in-memory — tinggal dicolok lewat dependency injection — refactor modul orders dari gaya service-repository menjadi port-adapter penuh di NestJS Fiber Go dan Laravel PHP lengkap dengan binding di service provider ditambah pembuktian testability total saat Postgres ditukar in-memory adapter sehingga seluruh suite use case lari dalam milidetik tanpa database

Modular monolith (ep.14) memberi kita batas antar fitur — tapi arah dependensi di dalam modul masih sering terbalik: service memanggil Prisma langsung, domain tahu tentang HTTP. Hexagonal architecture (Alistair Cockburn, 2005 — juga dikenal Ports & Adapters) memperbaiki arah itu secara radikal: logika inti tidak mengimpor apa pun dari dunia luar; dunia luar yang menempel pada inti melalui kontrak bernama port.
Dua jenis port, arah kebergantungan selalu masuk ke inti:
| Jenis | Arah | Contoh | Pemilik Interface |
|---|---|---|---|
| Driving (primary/kiri) | Aktor → inti | HTTP controller, CLI, test | Inti mendefinisikan API use case |
| Driven (secondary/kanan) | Inti → infrastruktur | Repository, mailer, payment gateway | Inti mendefinisikan interface, infra yang mengimplementasikan |
Kunci yang sering salah dipahami: port adalah milik domain, bukan cermin tabel database. OrderRepository didefinisikan dengan bahasa domain (simpan/hapus aggregate Order), bukan queryOrdersByStatusAndDateRange ala ORM.
1. Domain/application TIDAK mengimpor framework, ORM, atau driver mana pun
2. Driven port = interface di dalam inti; implementasi ada di adapter luar
3. Adapter menerjemahkan model: entity Prisma/gorm/Eloquent TIDAK boleh
bocor keluar adapter (mapping eksplisit, ingat anti-corruption)
4. Wiring dilakukan DI lapisan paling luar (main.ts / main.go / ServiceProvider)Imbalananya: testability total — seluruh use case bisa diuji dengan adapter palsu in-memory tanpa menyentuh jaringan, plus kemampuan plug-out riil (Postgres → in-memory, SMTP → logger) hanya dengan mengganti satu binding.
// domain/order/port.ts - MILIK INTI, nol import NestJS/Prisma:
export interface OrderRepository {
save(order: Order): Promise<void>;
byId(id: string): Promise<Order | null>;
}
export interface ReceiptMailer { send(email: string, orderId: string): Promise<void>; }
// application/place-order.usecase.ts - bergantung interface saja:
@Injectable()
export class PlaceOrderUseCase {
constructor(@Inject(ORDER_REPO) private repo: OrderRepository,
@Inject(MAILER) private mailer: ReceiptMailer) {}
}
// infrastructure/prisma-order.adapter.ts - ADAPTER mengimplementasikan:
@Injectable()
export class PrismaOrderRepository implements OrderRepository {
async save(order: Order) {
await this.prisma.order.create({ data: toRow(order) }); // mapping!
}
}
// presentation/orders.controller.ts - driving adapter:
@Post() create(@Body() dto: CreateOrderDto) {
return this.usecase.exec(dto);
}
// Wiring token <-> impl dilakukan di module providers (satu titik).Ketiganya menegakkan hukum yang sama: inti mendefinisikan kontrak, infrastruktur menurut. Framework kini adalah detail yang bisa diganti.
Target outline: refactor modul orders ke hexagonal, lalu swap Postgres → in-memory adapter untuk testing.
# Titik awal: modul orders gaya service-repository (ep.10)
1. Definisikan driven port di inti: OrderRepository (+ ReceiptMailer)
2. Pindahkan implementasi DB ke package adapter baru;
tambahkan mapping eksplisit row <-> aggregate (TANPA ubah perilaku)
3. Ganti semua import ORM di service/use case -> import port
4. Rewiring: token/binding/wiring di titik komposisi
5. Jalankan SELURUH test lama -> harus tetap hijau (refactor murni)
6. BUAT adapter InMemoryOrders (map sederhana di memori)
7. Di test use case: inject InMemoryOrders ->
suite lari milidetik tanpa Postgres. Itu bukti hexagonal bekerja.
8. Pasang gerbang arsitektur: grep/arch-test memastikan inti
tak lagi mengimpor prisma/gorm/eloquentTip
Ujian cepat arsitekturmu sekarang: bisakah kamu menulis test use case checkout tanpa membuka koneksi database? Jika ya, hexagonal-mu benar. Jika tidak, masih ada dependensi yang menyelinap lewat pintu belakang.
Rangkuman episode ini:
Episode 16 melangkah lebih jauh: Clean Architecture & Onion — lingkaran konsentris, The Dependency Rule, dan pragmatisme kapan ceremony-nya layak dibayar. Sampai jumpa!