Liskov Substitution dan jebakan inheritance yang melanggar kontrak, Interface Segregation melawan fat interface, Dependency Inversion sebagai pintu masuk hexagonal & DI — dengan token injection NestJS, constructor injection Fiber, dan contract binding Laravel

Tiga prinsip SOLID tersisa menutup fondasi desain series ini. LSP menjaga kejujuran hierarki, ISP menjaga interface tetap kecil, dan DIP — yang paling berdampak arsitektural — membalik arah dependency dan membuka pintu menuju hexagonal architecture (episode 15) serta dependency injection (episode 11).
LSP: subtype harus bisa menggantikan base type tanpa merusak ekspektasi klien — bukan hanya kompilasi, tapi perilaku kontraknya konsisten.
class Rectangle {
constructor(protected w: number, protected h: number) {}
setWidth(w: number): void { this.w = w; }
setHeight(h: number): void { this.h = h; }
area(): number { return this.w * this.h; }
}
class Square extends Rectangle {
// override yang "logis" tapi MERUSAK kontrak Rectangle:
setWidth(w: number): void { this.w = this.h = w; }
setHeight(h: number): void { this.w = this.h = h; }
}
function stretch(r: Rectangle): number {
r.setWidth(10); r.setHeight(5);
return r.area(); // klien Rectangle yakin hasilnya 50...
}
stretch(new Square()); // ...tapi dapat 25. Klien pecah diam-diam.Jebakan umum di backend nyata: subclass yang melempar exception baru untuk operasi yang dijamin base ("repository read-only yang throw pada save()"), atau memperketat preconditions secara diam.
Note
Solusi Square-vs-Rectangle bukan memperbaiki override, melainkan mengakui bahwa mereka dua tipe terpisah — atau sama sekali tidak pakai inheritance. LSP sering menjadi alasan kuat untuk komposisi.
Fat interface = satu kontrak besar yang memaksa semua implementer menyediakan hal yang tak dibutuhkan:
// SEBELUM - fat interface:
type Worker interface {
Start() error
Stop() error
HealthCheck() bool // tidak semua worker butuh ini
Metrics() Metrics // idem
}
// SESUDAH - segregate sesuai kebutuhan konsumen:
type Runner interface { Start() error }
type Stoppable interface { Stop() error }
type Observable interface { HealthCheck() bool }
// Implementer hanya memenuhi yang relevan; Go pun bahkan tanpa
// keyword implements - cukup punya method-nya (interface implisit).Aturan praktis ISP: interface didefinisikan oleh konsumennya — deklarasikan apa yang Anda butuhkan, bukan apa yang provider sediakan. Di TypeScript/PHP, implements eksplisit membuat god-interface lebih mudah terdeteksi saat implementer dipaksa menulis method kosong.
DIP adalah paling arsitektural dari kelima prinsip:
Inilah persis definisi ports & adapters: domain mendefinisikan port (PaymentGateway), infrastruktur menyediakan adapter. Detail lengkapnya di episode 15; hari ini kita fokus mekanisme wiring di ketiga stack.
abstract class PaymentGateway { // abstraction milik domain
abstract charge(amountCents: number): Promise<string>;
}
@Injectable()
export class StripeAdapter extends PaymentGateway {
async charge(amountCents: number): Promise<string> { /* stripe sdk */ }
}
@Module({
providers: [
CheckoutService,
{ provide: PaymentGateway, useClass: StripeAdapter }, // binding
],
})
export class CheckoutModule {}
// Ganti vendor = ganti useClass. CheckoutService tak pernah berubah (OCP!).Ketiganya mencapai efek yang sama: logika bisnis menyebut nama abstraksi, detail teknis disuntik dari luar.
Target outline: terapkan DIP agar service tidak tahu detail ORM.
1. Definisikan interface OrdersRepository di sisi service/domain
(method: Create, GetByID, ListByUser)
2. Implementasi v1 memakai ORM lab masing-masing
(TypeORM/Prisma | GORM | Eloquent)
3. Service HANYA menerima interface via constructor
4. Tulis unit test service dengan in-memory fake repo (tanpa DB!)
5. Ujian DIP: buat implementasi v2 in-memory, jalankan test service
TANPA mengubah satu baris service -> lolos = DIP bekerjaHasil akhir latihan ini adalah aset: service kini bisa ditest cepat tanpa database, dan keputusan ORM bisa ditunda/diganti — dua manfaat yang akan dipakai berulang sepanjang series.
Rangkuman episode ini:
SOLID tuntas! Episode 5 mulai tur katalog GoF dengan kelompok Creational: Factory Method, Abstract Factory, Builder, dan Singleton beserta kritik kontroversialnya. Sampai jumpa!