Dependency Injection

Dependency Injection #

Dalam perjalanan karier sebagai software engineer, hampir semua dari kita pernah menulis kode yang awalnya terlihat rapi, tapi beberapa bulan kemudian berubah menjadi mimpi buruk saat harus diuji, diubah, atau dikembangkan. Salah satu akar masalah yang paling sering saya temui adalah ketergantungan (dependency) yang saling mengikat terlalu kuat.

Di sinilah Dependency Injection (DI) muncul — bukan sebagai tren, tapi sebagai solusi nyata terhadap masalah desain software yang sudah ada sejak lama.

Apa itu Dependency Injection? #

Dependency Injection (DI) adalah sebuah design pattern di mana sebuah objek tidak membuat sendiri dependency yang ia butuhkan, melainkan dependency tersebut “disuntikkan” dari luar.

Contoh masalah tanpa DI #

type UserService struct {}

func (s *UserService) Register() {
    repo := NewUserRepository()
    repo.Save()
}

Di sini:

  • UserService secara langsung membuat UserRepository
  • UserService terikat kuat ke implementasi konkret

Artinya:

  • Sulit dites
  • Sulit diganti
  • Sulit dikembangkan

Dengan Dependency Injection #

type UserService struct {
    repo UserRepository
}

func NewUserService(repo UserRepository) *UserService {
    return &UserService{repo: repo}
}

Sekarang:

  • UserService tidak peduli bagaimana UserRepository dibuat
  • Ia hanya peduli pada kontrak (interface)

Intinya: Dependency Injection memindahkan tanggung jawab pembuatan dependency ke luar objek.


Kenapa ada Dependency Injection? #

Dependency Injection tidak muncul karena teori, tapi karena rasa sakit (pain) yang nyata dalam software development.

Masalah klasik tanpa DI #

  1. Tight Coupling

    • Class/service terlalu bergantung pada detail implementasi
  2. Sulit di-test

    • Unit test berubah jadi integration test
  3. Perubahan kecil → efek domino

    • Ganti database → ubah banyak kode
  4. Kode sulit dirawat

    • Engineer baru butuh waktu lama memahami alur

Dependency Injection hadir untuk menjawab: #

  • Bagaimana memisahkan business logic dari infrastructure
  • Bagaimana membuat kode fleksibel tanpa chaos
  • Bagaimana mendukung scalability tim, bukan hanya aplikasi

DI bukan tentang “lebih canggih”, tapi tentang mengurangi biaya perubahan.


Bagaimana DI mempengaruhi software development? #

Dampak DI tidak langsung terasa di awal, tapi sangat terasa di jangka menengah dan panjang.

Testability meningkat drastis #

Dengan DI:

mockRepo := new(MockUserRepository)
service := NewUserService(mockRepo)
  • Unit test benar-benar unit test
  • Tidak perlu database
  • Tidak perlu network

Tim yang serius soal testing hampir pasti menggunakan DI.


Separation of Concerns lebih jelas #

Dengan DI, biasanya arsitektur menjadi:

  • Controller / Handler
  • Service (business logic)
  • Repository (data access)
  • Infrastructure (DB, API, cache)

Setiap layer:

  • Fokus ke tanggung jawabnya
  • Tidak tahu detail layer lain

Ini membuat:

  • Kode lebih mudah dibaca
  • Diskusi desain lebih objektif
  • Refactor lebih aman

Perubahan jadi lebih murah #

Misalnya:

  • Ganti MySQL → PostgreSQL
  • Ganti REST → gRPC
  • Ganti third-party service

Dengan DI:

  • Yang berubah implementasi
  • Kontrak tetap sama

Tanpa DI:

  • Perubahan menyebar ke mana-mana

Skala tim lebih sehat #

DI memaksa kita berpikir dalam:

  • Interface
  • Kontrak
  • Boundary

Ini sangat membantu ketika:

  • Tim bertambah
  • Modul dikerjakan paralel
  • Ownership dibagi

Kenapa Dependency Injection adalah best practice? #

Dependency Injection disebut best practice bukan karena semua orang memakainya, tapi karena ia:

Selaras dengan prinsip SOLID #

  • S: Single Responsibility
  • O: Open/Closed
  • D: Dependency Inversion

DI adalah implementasi praktis dari Dependency Inversion Principle:

High-level modules should not depend on low-level modules.


Mendorong desain yang eksplisit #

Dengan DI:

  • Dependency terlihat jelas di constructor
  • Tidak ada “magic dependency” tersembunyi
  • Kode lebih jujur tentang kebutuhannya

Ini sangat penting untuk:

  • Code review
  • Onboarding engineer baru
  • Debugging

Industri sudah membuktikannya #

Framework besar menggunakan DI secara eksplisit atau implisit:

  • Spring (Java)
  • NestJS (Node.js)
  • ASP.NET Core
  • Uber FX (Go)
  • Google Wire (Go)

Bukan kebetulan — DI adalah fondasi arsitektur modern.


DI bukan over-engineering (jika digunakan benar) #

Kesalahan umum:

  • Menganggap DI = container kompleks
  • Menganggap DI selalu butuh framework

Padahal:

  • Constructor injection sederhana sudah DI
  • Tidak perlu container jika belum perlu

DI adalah soal mindset desain, bukan soal library.


Penutup #

Dependency Injection bukan solusi untuk semua masalah. Namun, tanpa DI, hampir semua sistem yang tumbuh akan:

  • Sulit diuji
  • Sulit diubah
  • Mahal dirawat

Sebagai software engineer, kita tidak dibayar hanya untuk membuat kode berjalan hari ini, tetapi untuk membuat sistem yang masih bisa diubah besok.

Dan di situlah Dependency Injection benar-benar menunjukkan nilainya.

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact