Langsung ke konten
Struktur Folder Proyek Backend yang Scalable: Peluang dari Sistem Enterprise yang Saya Kerjakan
Kembali ke Blog
Backend Development·6 menit baca·

Struktur Folder Proyek Backend yang Scalable: Peluang dari Sistem Enterprise yang Saya Kerjakan

Struktur folder backend yang scalable bukan soal estetika. Ini pengalaman saya merapikan proyek enterprise Node.js dari 200 file jadi arsitektur modular yang enak dirawat.

TH
Tomi Hartanto·Senior Software Engineer

Tim saya pernah mewarisi proyek backend Node.js dengan 200+ file TypeScript yang 80% di antaranya menumpuk di satu folder src/. Semua handler, query Prisma, helper, dan cron job aduk-aduk jadi satu. Setiap kali onboarding engineer baru, butuh dua minggu sebelum mereka berani menyentuh kode. Dua minggu itu mahal, dan masalahnya bukan kualitas kode — tapi struktur folder yang tidak mengikuti pertumbuhan sistem.

Setelah melewati tiga kali restuktur besar di proyek enterprise multi-tenant, saya punya pendapat yang cukup kuat soal ini: struktur folder adalah keputusan arsitektur, sama seriusnya dengan pemilihan database. Dan keputusan paling mahal biasanya bukan struktur yang salah sejak awal, tapi struktur yang tidak pernah diubah saat aplikasi tumbuh.

Kesalahan yang Paling Sering Saya Lihat: Folder Berdasarkan Jenis File

Banyak template starter pakai pola ini:

src/
  controllers/
  services/
  repositories/
  models/
  utils/

Pola ini dikenal sebagai layered by kind, dan untuk proyek kecil (< 30 file) sebenarnya tidak masalah. Tapi di sistem enterprise, pola ini cepat berubah jadi bencana. Untuk mengubah satu fitur "invoice", Anda harus buka controllers/invoice.ts, services/invoice.ts, repositories/invoice.ts, plus utils/invoiceCalc.ts. Satu perubahan bisnis tersebar di empat folder.

Masalah kedua: tidak ada batas yang jelas antar fitur. Service invoice dengan mudah meng-import service user, yang meng-import paymentService, yang balik lagi ke invoice — dependency graph yang lama-lama jadi spaghetti yang tidak bisa dipotong.

Diagram perbandingan struktur folder by-kind versus by-feature dengan panah dependency

Struktur yang Akhirnya Saya Pakai: Modular by Feature

Setelah dua kali gagal refactor, struktur yang bertahan di produksi untuk sistem multi-tenant dengan 15+ engineer adalah modular by feature:

src/
  modules/
    auth/
      auth.routes.ts
      auth.controller.ts
      auth.service.ts
      auth.repository.ts
      auth.schema.ts        # Zod/DTO
      auth.test.ts
      __mocks__/
    invoice/
      invoice.routes.ts
      invoice.controller.ts
      invoice.service.ts
      invoice.repository.ts
      invoice.events.ts    # publisher/subscriber
    tenant/
      ...
  shared/
    db/          # Prisma client, transaction helper
    logger/
    errors/      # AppError, error codes
    queue/       # koneksi BullMQ
  app.ts
  server.ts

Prinsip dasarnya satu: semua yang berubah bersama, diletakkan bersama. Kalau product manager minta perubahan logika invoice, 95% diff harus berada dalam satu folder. PR jadi kecil, code review jadi cepat, dan git log -- src/modules/invoice langsung memberi riwayat fitur itu.

Kalau Anda belum terbiasa dengan pola pemisahan seperti ini, best practice REST API design yang pernah saya tulis cocok dibaca dulu karena struktur folder dan desain endpoint saling mengikat.

Aturan Import: Kunci yang Sering Dilupakan

Struktur folder tidak akan berguna tanpa aturan dependency. Yang saya pakai:

  1. modules/* boleh meng-import shared/* — boleh.
  2. modules/auth boleh meng-import modules/tenant HANYA lewat index publik (modules/tenant/index.ts).
  3. shared/* dilarang keras meng-import modules/*.

Untuk memaksa aturan ini, saya pakai eslint-plugin-import dengan no-restricted-imports:

{
  "rules": {
    "no-restricted-imports": ["error", {
      "patterns": [{
        "group": ["**/shared/**"],
        "message": "shared/ tidak boleh import dari modules/"
      }]
    }]
  }
}

Tanpa linter, aturan arsitektur hanya ada di kepala senior engineer — dan lupa begitu orang baru masuk.

Kapan Layer Terpisah Masih Masuk Akal

Saya tidak mau terdengar seperti salesman modular. Ada kasus di mana shared/ membesar sendiri — misalnya logger, koneksi database, dan helper queue jadi proyek mini. Di satu proyek besar, kami akhirnya mengekstrak shared/ jadi private package npm terpisah (@perusahaan/db, @perusahaan/logger). Itu membantu karena konsumen package-nya bukan cuma satu service.

Tapi untuk monolit satu service, jangan buru-buru bikin monorepo. Ekstraksi package itu punya overhead tersendiri (versi, publish pipeline, sinkronisasi release). Kalau service Anda cuma satu dan timnya di bawah 20 orang, folder shared/ biasa sudah cukup.

Begitu tim tumbuh dan domain antar modul makin berbeda kebutuhannya, struktur modular ini juga jadi batu pijakan alami menuju microservices — tiap folder modules/* adalah kandidat ekstraksi. Prinsip serupa juga saya bahas di strategi refactoring legacy code tanpa menghentikan bisnis, karena merestrukturisasi folder pada sistem hidup pada dasarnya adalah refactor, bukan rewrite.

Detail Kecil yang Berdampak Besar

Beberapa keputusan kecil yang terbukti berpengaruh di proyek nyata:

File masuk ke queue, bukan blok event loop

Modul besar pasti punya task berat — export laporan, kirim email massal. Jangan pernah taruh logika ini di controller. Kami konsisten menaruhnya di *.events.ts atau dedicated worker folder, dengan BullMQ untuk background job dan queue yang berjalan di proses terpisah dari HTTP server. Struktur folder yang memisahkan worker/ dari api/ membuat deployment scaling-nya juga jelas: naikkan replika API tanpa menaikkan worker, atau sebaliknya.

apps/
  api/        # HTTP server
  worker/     # BullMQ worker
packages/
  modules/    # domain logic, dipakai api & worker

Validasi tinggal di pintu masuk

Setiap modul punya *.schema.ts sendiri yang mendefinisikan bentuk input. Ini konsisten dengan prinsip never trust the client di validasi input backend — schema hidup berdampingan dengan modul yang memakainya, bukan terkumpul di satu folder validations/ raksasa.

Naming yang membosankan itu bagus

Saya standarkan nama file: <nama>.routes.ts, <nama>.service.ts, <nama>.repository.ts. Tidak kreatif? Justru itu intinya. Konvensi yang membosankan membuat orang bisa menebak lokasi file tanpa membaca dokumentasi. Ctrl+P + "invoice.service" selalu mengarah ke satu file yang benar.

Ilustrasi struktur folder modular backend dengan modul fitur terisolasi dan shared utilities

Proses Migrasi Tanpa Drama

Kalau Anda sudah punya proyek dengan struktur berantakan, jangan refactor sekaligus. Urutan yang saya pakai:

  1. Bekukan aturan baru — semua file baru wajib masuk struktur modular, file lama dibiarkan.
  2. Pindahkan modul paling sering berubah duluan — ini yang memberi ROI tercepat.
  3. Gunakan git mv supaya riwayat file terjaga dan blame tetap akurat.
  4. Pindah bertahap per modul, satu PR per modul, maksimal 300 baris diff per PR.

Di proyek terakhir, migrasi penuh 200 file memakan enam minggu tanpa menghentikan feature delivery. Kuncinya bukan cepat, tapi tiap langkah selalu deployable.

Perlu diakui: ada trade-off nyata. Struktur modular duplikasi sedikit kode antar modul (misalnya dua modul punya helper pagination mirip), dan untuk tim kecil 2-3 orang, overhead konsistensi ini bisa terasa berlebihan. Kalau proyek Anda diperkirakan selesai dalam 3 bulan dan tidak akan disentuh lagi — layered sederhana masih sah.

Takeaway yang Bisa Anda Pakai Hari Ini

  • Kalau diff satu perubahan fitur selalu menyentuh 4+ folder, itu sinyal struktur Anda sudah tidak scalable — bukan sinyal developer Anda kurang disiplin.
  • Mulailah dengan modules/ + shared/, paksa boundary pakai ESLint, jangan pakai kepercayaan.
  • Standarkan naming file sampai membosankan; itu fitur, bukan bug.
  • Pisahkan api/ dan worker/ sejak awal meskipun masih satu repo — Anda akan berterima kasih saat scaling pertama kali.
  • Restrukturisasi itu proyek bertahap, bukan big-bang rewrite.

Struktur folder yang baik tidak membuat aplikasi Anda cepat secara ajaib, tapi ia menentukan seberapa cepat tim Anda bisa berubah — dan di jangka panjang, itu yang paling mahal.

TH

Tomi Hartanto

Senior Software Engineer

Backend developer dengan 8+ tahun pengalaman membangun sistem scalable. Menulis tentang arsitektur backend, database, dan DevOps.