SRP

SRP #

Single Responsibility Principle (SRP) adalah prinsip pertama dari SOLID Principles yang sangat fundamental dalam software engineering. SRP membantu kita membangun kode yang mudah dipahami, mudah dirawat (maintainable), dan mudah dikembangkan seiring waktu.

Dalam praktik sehari-hari, banyak masalah teknis seperti kode sulit diubah, bug muncul di tempat tak terduga, atau perubahan kecil berdampak besar—berasal dari pelanggaran SRP.

Artikel ini akan membahas:

  • Apa itu SRP dan makna sebenarnya
  • Kenapa SRP penting
  • Ciri-ciri pelanggaran SRP
  • Contoh tanpa SRP dan dengan SRP
  • Implementasi SRP yang idiomatis di Golang
  • Best practice penerapan SRP di sistem nyata

Apa Itu Single Responsibility Principle? #

Definisi klasik SRP dari Robert C. Martin:

“A class should have only one reason to change.”

Artinya:

  • Satu modul / struct / class hanya boleh punya satu tanggung jawab utama
  • Jika ada perubahan kebutuhan, hanya satu alasan yang valid untuk mengubah modul tersebut

⚠️ SRP bukan berarti satu fungsi satu baris, atau satu file satu fungsi. SRP berbicara tentang alasan perubahan (reason to change), bukan ukuran kode.


Kenapa SRP Itu Penting? #

Kode Lebih Mudah Dipahami #

Saat membaca kode:

  • Kita langsung tahu apa tugas utama komponen ini
  • Tidak perlu memahami konteks lain yang tidak relevan

Perubahan Lebih Aman #

Jika satu tanggung jawab berubah:

  • Kita tahu file mana yang harus diubah
  • Risiko efek samping ke fitur lain lebih kecil

Testing Lebih Mudah #

  • Unit test lebih fokus
  • Mocking dan dependency injection lebih sederhana

Mendukung Scalability Tim #

  • Banyak engineer bisa bekerja paralel
  • Konflik kode (merge conflict) berkurang

Ciri-ciri Kode yang Melanggar SRP #

Beberapa red flag umum:

  • Satu struct melakukan:

    • validasi
    • business logic
    • akses database
    • logging
    • formatting response
  • Satu file sering diubah karena alasan yang berbeda-beda

  • Banyak if berdasarkan tipe atau kondisi yang tidak berhubungan

  • Unit test sulit dibuat tanpa setup kompleks


Contoh Pelanggaran SRP (Anti-Pattern) #

Misalkan kita punya service user sederhana di Golang:

package user

import (
    "database/sql"
    "fmt"
)

type UserService struct {
    db *sql.DB
}

func (s *UserService) Register(name, email string) error {
    // Validasi
    if name == "" || email == "" {
        return fmt.Errorf("invalid input")
    }

    // Simpan ke database
    _, err := s.db.Exec(
        "INSERT INTO users (name, email) VALUES (?, ?)",
        name, email,
    )
    if err != nil {
        return err
    }

    // Logging
    fmt.Println("User registered:", email)

    // Kirim email
    fmt.Println("Sending welcome email to", email)

    return nil
}

Masalah pada Kode di Atas #

UserService memiliki banyak tanggung jawab:

  1. Validasi input
  2. Business logic user
  3. Akses database
  4. Logging
  5. Email notification

Akibatnya:

  • Perubahan format email → ubah UserService
  • Perubahan database → ubah UserService
  • Perubahan aturan validasi → ubah UserService

Banyak alasan untuk berubah = pelanggaran SRP


Refactor Menggunakan SRP #

Mari kita pecah berdasarkan tanggung jawab.

Validasi User #

package user

type Validator struct {}

func (v *Validator) Validate(name, email string) error {
    if name == "" || email == "" {
        return fmt.Errorf("invalid input")
    }
    return nil
}

Tanggung jawab tunggal: validasi data user

Repository (Akses Data) #

package user

import "database/sql"

type Repository struct {
    db *sql.DB
}

func (r *Repository) Save(name, email string) error {
    _, err := r.db.Exec(
        "INSERT INTO users (name, email) VALUES (?, ?)",
        name, email,
    )
    return err
}

Tanggung jawab tunggal: persistensi data user

Email Service #

package notification

import "fmt"

type EmailService struct {}

func (e *EmailService) SendWelcome(email string) {
    fmt.Println("Sending welcome email to", email)
}

Tanggung jawab tunggal: mengirim email

Logger #

package logger

import "fmt"

type Logger struct {}

func (l *Logger) Info(message string) {
    fmt.Println("INFO:", message)
}

Tanggung jawab tunggal: logging

User Service (Orchestrator) #

package user

type Service struct {
    validator *Validator
    repo      *Repository
    email     *notification.EmailService
    logger    *logger.Logger
}

func (s *Service) Register(name, email string) error {
    if err := s.validator.Validate(name, email); err != nil {
        return err
    }

    if err := s.repo.Save(name, email); err != nil {
        return err
    }

    s.email.SendWelcome(email)
    s.logger.Info("User registered: " + email)

    return nil
}

Kenapa Ini Sudah Mengikuti SRP? #

  • Setiap struct punya satu tanggung jawab jelas
  • Perubahan email tidak memengaruhi repository
  • Perubahan database tidak memengaruhi validasi
  • Service hanya bertugas mengorkestrasi flow, bukan mengerjakan detail

SRP dan Idiom Golang #

Di Golang:

  • SRP tidak selalu berbentuk class

  • Bisa berupa:

    • struct
    • package
    • bahkan satu file

Prinsip praktis:

Jika satu package sering berubah karena alasan berbeda, itu tanda SRP dilanggar


Hubungan SRP dengan Prinsip Lain #

  • SRP + Dependency Injection → unit test lebih mudah
  • SRP + OCP → fitur baru tanpa ubah kode lama
  • SRP + Clean Architecture → layer jelas dan terpisah

SRP sering menjadi fondasi untuk prinsip-prinsip lain berjalan dengan baik.


Best Practice Menerapkan SRP #

  1. Fokus pada reason to change, bukan jumlah method

  2. Pisahkan:

    • business logic
    • infrastructure
    • side effects (email, log, external call)
  3. Gunakan interface untuk boundary

  4. Jangan takut menambah file — takutlah pada file yang isinya terlalu banyak

  5. Refactor bertahap, tidak harus langsung sempurna


Penutup #

Single Responsibility Principle bukan soal gaya menulis kode, tapi soal cara berpikir dalam mendesain sistem.

Dengan SRP:

  • Kode lebih rapi
  • Perubahan lebih aman
  • Testing lebih mudah
  • Sistem lebih siap tumbuh
About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact