Clean Architecture dan debat Repository di atas EF Core
Ringkasan satu halaman. Dari Lesson 0011. Layered: reference layered · DIP: reference SOLID.
Gambar
UI (Controllers, Program.cs) ──► Application Core ◄── Infrastructure (AppDbContext, Migrations,
(Entity, interface, ProductRepository, SmtpNotifier)
domain service, DTO)
panah = dependency compile-time; Core tidak mereferensikan apa pun
Dependency rule
"source code dependencies can only point inwards." [2]
Caranya DIP: interface di Core, implementasi di Infrastructure, disambungkan di composition root (Program.cs). UI boleh mereferensikan Infrastructure hanya untuk wiring DI. [1]
Nama lain: Hexagonal, Ports-and-Adapters, Onion.
Layered vs Clean
| Layered | Clean | Padanan Node | |
|---|---|---|---|
| Arah | UI → BLL → DAL (bisnis bergantung pada data access) [1] | Semua ke dalam, ke Application Core | routes → services → prisma vs ports & adapters (NestJS) |
| Penjaga arah | Disiplin folder | Referensi antar project | Lint rule import vs package terpisah |
| Cocok untuk | CRUD kecil-menengah | Domain kompleks, berumur panjang |
Repository di atas EF Core
| Pro | Kontra | Kapan tidak perlu |
|---|---|---|
Rekomendasi EF Core bila memakai test double: mock IProductRepository, bukan DbSet / in-memory provider [4]. Query terkumpul di satu tempat. Contoh nyata DIP. |
"DbContext is a combination of the Unit Of Work and Repository patterns." [3] Repository generik = boilerplate. Biaya implementasi dan perawatan "significant". [4] | CRUD kecil; tim yang memilih integration test ke database nyata (Testcontainers / SQLite). Repository "isn't an essential pattern". [5] |
Jebakan: repository yang mengembalikan IQueryable membocorkan abstraksi; EF Core tetap menerjemahkan query. Kembalikan hasil jadi (IReadOnlyList, IAsyncEnumerable, Product?). [6]
Jawaban 60 detik: "Repository di atas EF Core, perlu?"
Tergantung. Pro: EF Core merekomendasikannya kalau memakai test double, jadi unit test Service cukup mock IProductRepository; query terkumpul di satu tempat; contoh DIP.
Kontra: DbContext sudah Unit of Work + Repository, repository generik hanya boilerplate, dan IQueryable membocorkan abstraksi.
Tidak perlu untuk CRUD kecil atau tim yang memakai integration test ke database nyata. Posisi saya: repository spesifik per Entity dengan method yang mengembalikan hasil jadi.
Di Node: membungkus Prisma client, yang sendiri sudah mirip repository.
Pertanyaan lanjutan yang mungkin muncul
- "DTO di mana?" → Application Core, sebagai tipe sederhana tanpa dependency ke UI/Infrastructure. [1]
- "Kenapa Core mudah di-unit-test?" → "Because the Application Core doesn't depend on Infrastructure". [1]
- "Harus empat lingkaran?" → Tidak, "the circles are schematic". [2]
- "Satu repository per tabel?" → Dalam DDD, satu per aggregate root, bukan per tabel. [5]
- "CQRS / MediatR?" → Cukup kenal namanya; bukan syarat Clean Architecture.
Sumber: [1] Common web application architectures · [2] Robert C. Martin: The Clean Architecture · [3] EF Core API: DbContext · [4] EF Core: Choosing a testing strategy · [5] Designing the infrastructure persistence layer · [6] EF Core: Testing without your production database system