Layered N-tier architecture: presentation, application/business, data access, database — aturan dependensi satu arah, horizontal slicing dan konsekuensinya, struktur module NestJS, pkg handler service repository Fiber, app Http Services Repositories Laravel — praktik rebuild fitur episode 10 plus lint aturan dependensi

Sembilan episode terakhir menyusun potongan-potongan (DTO, repository, service, DI). Sekarang kita susun menjadi bangunan pertama yang utuh: Layered Architecture — pola paling tua, paling umum, dan tetap menjadi titik awal default sebagian besar aplikasi backend di 2026.
Satu aturan emas yang menopang semuanya: dependensi hanya boleh mengalir ke bawah. Presentation tahu service; service tahu data access; data access tidak pernah tahu ada HTTP. Layer tidak boleh melompat dua tingkat ke bawah secara langsung (controller memanggil ORM = pelanggaran).
Layered mengiris kode secara horizontal: semua controller bersama, semua service bersama, semua repo bersama. Kelebihannya nyata: konvensi jelas, onboarding cepat, cocok untuk tim besar dengan spesialisasi layer.
Konsekuensi yang juga harus disadari:
src/
├── orders/
│ ├── controllers/ # presentation
│ │ └── orders.controller.ts
│ ├── dto/
│ ├── services/ # application
│ │ └── checkout.service.ts
│ ├── repositories/ # data access
│ │ └── prisma-orders.repository.ts
│ └── domain/ # entity + interface milik domain
│ ├── order.entity.ts
│ └── orders.repository.ts # interface DI SINI (DIP!)
└── users/ ...Perhatikan posisi interface repository selalu di sisi domain/service (DIP) — implementasi ORM-lah yang bergantung pada kontrak, bukan sebaliknya.
Target outline: bangun ulang fitur episode 10 sebagai layered penuh + tulis aturan lint dependency.
module.exports = {
forbidden: [
{
name: 'no-data-access-in-presentation',
comment: 'Controller dilarang import repository/ORM langsung',
from: { path: '^src/.*/controllers/' },
to: {
pathNot: ['^src/(.*/)?services/'],
path: '(repositories|prisma|typeorm|gorm)',
},
},
{
name: 'domain-must-not-know-framework',
comment: 'Domain murni: tanpa import framework/HTTP/ORM',
from: { path: '^src/orders/domain/' },
to: { path: '(@nestjs|express|prisma|typeorm|fiber)' },
},
],
};
// npx depcruise src --validate .dependency-cruiser.cjs -> CI gateUntuk Laravel, padanannya bisa lewat aturan sederhana di CI:
# Controller TIDAK boleh menyentuh Model/Eloquent langsung:
! grep -rn "Eloquent\|::find\|::where" app/Http/Controllers \
|| (echo "VIOLATION: controller menyentuh data access" && exit 1)Checklist verifikasi rebuild:
[x] Fitur registrasi (ep.10) dipindah ke struktur layered penuh
[x] Interface repo hidup di domain; impl ORM di layer bawah
[x] Controller bebas import ORM/repository (dibuktikan lint)
[x] Semua test lama tetap hijau tanpa ubahan logika
[x] Commit "ep13: restructure to layered + lint gate"Tip
Aturan arsitektur yang tidak diotomasi akan dilanggar dalam hitungan minggu. Lint dependency adalah bentuk termurah "architecture as code".
Rangkuman episode ini:
Episode 14 menghadapi kelemahan itu langsung: Modular Monolith — package by feature, boundary antar modul, dan jalan menuju microservices. Sampai jumpa!