Portal/Notes πŸ“
Programming

Decoupling

Memahami decoupling dalam rekayasa perangkat lunak β€” mengurangi ketergantungan antar komponen untuk modularitas, skalabilitas, dan maintainability.

Versi Bahasa Indonesia. Baca dalam Bahasa Inggris

Decoupling dalam rekayasa perangkat lunak mengacu pada proses mengurangi ketergantungan antar komponen, sistem, atau lapisan dalam aplikasi. Tujuannya adalah memastikan bahwa perubahan di satu bagian sistem memiliki dampak minimal atau tidak sama sekali pada bagian lain, mendorong modularitas, skalabilitas, dan kemudahan pemeliharaan.

  • Low Coupling: Komponen berinteraksi satu sama lain secara minimal dan dengan cara yang terdefinisi dengan baik. Ketergantungan antar komponen dikurangi atau diabstraksi.
  • High Cohesion: Setiap komponen fokus pada satu tanggung jawab atau tugas yang terkait erat.
  • Abstraksi: Interface, design pattern, atau API sering digunakan untuk memisahkan concern dan mendecouple komponen.
  • Komunikasi melalui Kontrak: Komponen berinteraksi menggunakan protokol atau kontrak yang terdefinisi dengan baik, seperti interface atau pesan.

Manfaat

  • Kemudahan Pemeliharaan: Perubahan di satu modul cenderung tidak mempengaruhi modul lain.
  • Reusability: Komponen yang terdecouple dapat digunakan kembali di proyek atau konteks lain.
  • Testability: Komponen yang terisolasi lebih mudah diuji secara independen.
  • Skalabilitas: Sistem yang terdecouple dapat diskalakan dengan mengganti atau meningkatkan komponen individu tanpa mengganggu yang lain.
  • Fleksibilitas: Lebih mudah untuk menukar atau mengganti satu komponen dengan yang lain.

Teknik Decoupling

  • Abstraksi:
    • Gunakan interface untuk mendefinisikan perilaku.
    • Contoh: Decouple logika bisnis dari logika akses data dengan mendefinisikan repository interface.
  • Event-Driven Architecture:
    • Komponen berkomunikasi melalui event, bukan panggilan langsung.
    • Contoh: Sistem publish/subscribe.
  • Dependency Injection (DI):
    • Suntikkan dependensi ke dalam komponen, bukan membuat instance di dalamnya.
    • Contoh: Berikan dependensi database ke dalam constructor service.
  • Message Queues:
    • Gunakan sistem messaging (misalnya RabbitMQ, Kafka) untuk komunikasi asinkron antar komponen yang terdecouple.

Contoh Dunia Nyata

  • Microservices: Setiap microservice adalah unit mandiri dengan database dan logikanya sendiri. Service berkomunikasi melalui API atau sistem messaging, bukan panggilan langsung.
  • Frontend dan Backend: Terdecouple melalui RESTful API atau GraphQL, memungkinkan pengembangan dan penskalaan independen.
  • Plugin dan Ekstensi: Sistem yang terdecouple memungkinkan fitur baru ditambahkan tanpa mengubah aplikasi inti.

Kapan Decoupling Berlebihan

  • Aplikasi Kecil: Decoupling dapat menambah kompleksitas yang tidak perlu.
  • Kekhawatiran Performa: Abstraksi tambahan atau passing pesan dapat menambah latensi.

Contoh Golang

Sebelum Decoupling (Sistem Tightly Coupled)

type MySQLDatabase struct{}

func (db MySQLDatabase) Connect() {
    fmt.Println("Connecting to MySQL...")
}

type UserService struct {
    Database MySQLDatabase
}

func (s UserService) GetUser(userID int) {
    s.Database.Connect()
    fmt.Printf("Fetching user with ID: %d\n", userID)
}

func main() {
    service := UserService{Database: MySQLDatabase{}}
    service.GetUser(1)
}

Masalah: UserService terikat erat dengan MySQLDatabase. Jika kita ingin beralih ke database lain (misalnya PostgreSQL), kita harus memodifikasi kode UserService.

Setelah Decoupling (Menggunakan Abstraksi)

type Database interface {
    Connect()
}

type MySQLDatabase struct{}

func (db MySQLDatabase) Connect() {
    fmt.Println("Connecting to MySQL...")
}

type PostgreSQLDatabase struct{}

func (db PostgreSQLDatabase) Connect() {
    fmt.Println("Connecting to PostgreSQL...")
}

type UserService struct {
    Database Database
}

func (s UserService) GetUser(userID int) {
    s.Database.Connect()
    fmt.Printf("Fetching user with ID: %d\n", userID)
}

func main() {
    // Beralih antara implementasi database yang berbeda tanpa mengubah UserService
    mysql := MySQLDatabase{}
    postgres := PostgreSQLDatabase{}

    service := UserService{Database: mysql}
    service.GetUser(1)

    service.Database = postgres
    service.GetUser(2)
}

Solusi: UserService sekarang terdecouple dari implementasi database tertentu. Ia bergantung pada interface Database, membuatnya mudah untuk mengganti atau menambah database baru.


Edit on GitHub

Last updated on

Decoupling | Faisal Affan