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
ifberdasarkan tipe atau kondisi yang tidak berhubunganUnit 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:
- Validasi input
- Business logic user
- Akses database
- Logging
- 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
Servicehanya 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 #
Fokus pada reason to change, bukan jumlah method
Pisahkan:
- business logic
- infrastructure
- side effects (email, log, external call)
Gunakan interface untuk boundary
Jangan takut menambah file — takutlah pada file yang isinya terlalu banyak
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