SoC

SoC #

Separation of Concerns (SoC) adalah salah satu prinsip fundamental dalam software engineering yang menekankan bahwa sebuah sistem harus dipisahkan ke dalam bagian-bagian (concerns) yang masing-masing menangani satu tanggung jawab spesifik. Setiap bagian fokus pada apa yang menjadi urusannya sendiri, tanpa mencampuri urusan bagian lain.

Prinsip ini pertama kali diperkenalkan oleh Edsger W. Dijkstra, dan hingga hari ini menjadi fondasi bagi berbagai konsep modern seperti clean architecture, layered architecture, microservices, dan bahkan framework web populer.

Secara sederhana:

Satu modul, satu tanggung jawab utama.

Apa yang Dimaksud dengan “Concern”? #

Concern adalah aspek atau tanggung jawab tertentu dalam sebuah aplikasi. Contoh concern dalam aplikasi backend:

  • Logika bisnis (business rules)
  • Akses database
  • HTTP handling (request/response)
  • Validasi input
  • Autentikasi & otorisasi
  • Logging
  • Configuration

Masalah umum muncul ketika semua concern ini dicampur dalam satu tempat, misalnya satu function HTTP handler yang:

  • parsing request
  • validasi
  • query database
  • logika bisnis
  • formatting response

Inilah yang ingin dihindari oleh SoC.


Tujuan dan Manfaat Separation of Concerns #

Maintainability #

Perubahan pada satu concern tidak merusak bagian lain.

Readability #

Kode lebih mudah dibaca karena setiap bagian punya fokus jelas.

Testability #

Business logic dapat diuji tanpa HTTP server atau database sungguhan.

Reusability #

Concern yang terpisah bisa digunakan ulang di konteks lain.

Scalability (Secara Tim & Kode) #

Tim bisa bekerja paralel pada concern yang berbeda.


SoC vs Prinsip Lain #

SoC sering berkaitan dengan prinsip lain:

  • SRP (Single Responsibility Principle) → fokus pada class/module
  • SoC → fokus pada pemisahan aspek sistem secara keseluruhan
  • Layered Architecture → salah satu implementasi SoC

SoC adalah konsep besar, SRP adalah penerapannya di level lebih kecil.


Contoh Buruk: Tanpa Separation of Concerns (Golang) #

func CreateUserHandler(w http.ResponseWriter, r *http.Request) {
    // parsing request
    var req CreateUserRequest
    json.NewDecoder(r.Body).Decode(&req)

    if req.Email == "" {
        http.Error(w, "email required", http.StatusBadRequest)
        return
    }

    // business logic
    hashedPassword := hashPassword(req.Password)

    // database logic
    db.Exec("INSERT INTO users(email, password) VALUES (?, ?)", req.Email, hashedPassword)

    // response
    json.NewEncoder(w).Encode(map[string]string{"status": "ok"})
}

Masalah: #

  • HTTP logic bercampur dengan business logic
  • Sulit di-test tanpa database
  • Sulit digunakan ulang di konteks lain (CLI, worker, dll)

Contoh Baik: Penerapan SoC dengan Layered Approach #

Kita pisahkan concern menjadi beberapa layer:

/handler      -> HTTP layer
/service      -> business logic
/repository   -> database access
/domain       -> entity & rule

Domain Layer (Entity) #

// domain/user.go
package domain

type User struct {
    ID       int64
    Email    string
    Password string
}

Concern:

  • Representasi data dan aturan inti

Repository Layer (Database Concern) #

// repository/user_repository.go
package repository

import "database/sql"

 type UserRepository interface {
    Save(user *domain.User) error
 }

 type userRepository struct {
    db *sql.DB
 }

 func NewUserRepository(db *sql.DB) UserRepository {
    return &userRepository{db: db}
 }

 func (r *userRepository) Save(user *domain.User) error {
    _, err := r.db.Exec(
        "INSERT INTO users(email, password) VALUES (?, ?)",
        user.Email,
        user.Password,
    )
    return err
 }

Concern:

  • Hanya tahu cara menyimpan data
  • Tidak tahu HTTP atau business rules

Service Layer (Business Logic Concern) #

// service/user_service.go
package service

 type UserService interface {
    CreateUser(email, password string) error
 }

 type userService struct {
    repo repository.UserRepository
 }

 func NewUserService(repo repository.UserRepository) UserService {
    return &userService{repo: repo}
 }

 func (s *userService) CreateUser(email, password string) error {
    if email == "" {
        return errors.New("email is required")
    }

    hashed := hashPassword(password)

    user := &domain.User{
        Email:    email,
        Password: hashed,
    }

    return s.repo.Save(user)
 }

Concern:

  • Aturan bisnis
  • Tidak tahu HTTP atau database detail

Handler Layer (HTTP Concern) #

// handler/user_handler.go
package handler

 type UserHandler struct {
    service service.UserService
 }

 func NewUserHandler(s service.UserService) *UserHandler {
    return &UserHandler{service: s}
 }

 func (h *UserHandler) Create(w http.ResponseWriter, r *http.Request) {
    var req CreateUserRequest
    json.NewDecoder(r.Body).Decode(&req)

    err := h.service.CreateUser(req.Email, req.Password)
    if err != nil {
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }

    json.NewEncoder(w).Encode(map[string]string{"status": "ok"})
 }

Concern:

  • HTTP request/response
  • Tidak tahu detail hashing atau SQL

Dampak Positif dari SoC #

Unit Test Lebih Mudah #

func TestCreateUser(t *testing.T) {
    repo := new(MockUserRepository)
    service := NewUserService(repo)

    err := service.CreateUser("[email protected]", "password")

    assert.NoError(t, err)
}

Tanpa HTTP server, tanpa database sungguhan.


SoC di Dunia Nyata #

SoC tidak hanya soal folder atau package, tapi soal batas tanggung jawab:

  • Controller vs Service vs Repository
  • API Gateway vs Backend Service
  • Service A (Auth) vs Service B (Payment)
  • Synchronous API vs Async Worker

Di arsitektur microservices, SoC sering diterjemahkan menjadi service-level separation.


Kesalahan Umum Saat Menerapkan SoC #

  • Over-engineering (terlalu banyak layer tanpa kebutuhan)
  • Menganggap SoC = banyak folder
  • Business logic bocor ke handler
  • Repository berisi logic bisnis

SoC harus proporsional dengan kompleksitas sistem.


Kapan SoC Sangat Penting? #

  • Aplikasi jangka panjang
  • Tim besar
  • Sistem dengan perubahan bisnis cepat
  • Aplikasi dengan banyak integrasi

Untuk aplikasi kecil, SoC bisa diterapkan secara bertahap.


Kesimpulan #

Separation of Concerns adalah prinsip inti yang membantu kita membangun sistem:

  • Lebih rapi
  • Mudah diuji
  • Mudah dikembangkan
  • Tahan terhadap perubahan

Dalam Golang, SoC sangat natural diterapkan melalui package, interface, dan dependency injection sederhana.

Pisahkan concern, bukan sekadar file.

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