Design Pattern & Software Architecture #
Setiap engineer yang sudah cukup lama menulis kode pasti pernah mengalami momen yang sama: melihat kembali kode yang ditulis enam bulan lalu dan tidak mengerti sendiri. Atau mencoba menambahkan fitur kecil tapi ternyata mengharuskan perubahan di sepuluh tempat berbeda. Atau berdiskusi desain dengan rekan tim tapi masing-masing membayangkan solusi yang berbeda-beda, padahal sedang membicarakan masalah yang sama. Masalah-masalah ini bukan tentang kemampuan teknis — ini tentang kurangnya bahasa dan kerangka bersama untuk membicarakan struktur kode. Design pattern dan software architecture hadir untuk mengisi kekosongan itu: bukan sebagai resep ajaib, melainkan sebagai kosakata dan pola pikir yang memungkinkan kamu merancang sistem yang tahan lama.
Tujuan Website Ini #
Banyak sumber belajar design pattern yang langsung menampilkan kode implementasi tanpa menjelaskan mengapa pattern tersebut ada dan kapan ia tepat digunakan. Hasilnya, pattern dipelajari sebagai hafalan, bukan sebagai alat berpikir. Website ini mengambil pendekatan berbeda.
Setiap topik dibahas dari akar masalahnya: masalah apa yang memunculkan pattern ini, bagaimana solusi naif gagal, dan mengapa pattern tersebut menjadi jawaban yang lebih baik. Implementasi hadir sebagai bukti konsep, bukan tujuan utama.
Ada empat hal yang ingin dicapai dari setiap pembaca:
flowchart LR
A[Membaca\nWebsite Ini] --> B[Memahami\nMengapa Pattern Ada]
B --> C[Mengenali\nKapan Pattern Tepat]
C --> D[Membangun\nMental Model Desain]
D --> E[Merancang Sistem\nyang Tahan Lama]
Memahami mengapa — Pattern bukan pengetahuan trivia. Setiap pattern lahir dari pengalaman nyata menghadapi masalah berulang. Memahami konteks kelahirannya jauh lebih penting dari menghapal namanya.
Mengenali kapan — Pattern yang tepat di situasi yang salah lebih berbahaya dari tidak menggunakan pattern sama sekali. Kamu perlu tahu kapan menggunakan dan kapan tidak menggunakan pattern tertentu.
Membangun mental model — Yang paling berharga dari belajar design pattern bukan daftar pattern yang kamu hapal, melainkan cara berpikirmu berubah saat menghadapi masalah desain baru.
Merancang sistem yang tahan lama — Kode yang baik bukan kode yang bekerja hari ini, tapi kode yang masih bisa dikembangkan, diuji, dan dipahami enam bulan dari sekarang.
Apa Itu Design Pattern? #
Design pattern adalah template solusi untuk masalah desain yang berulang dalam pengembangan perangkat lunak. Konsep ini dipopulerkan oleh buku Design Patterns: Elements of Reusable Object-Oriented Software (1994) oleh Gang of Four — Erich Gamma, Richard Helm, Ralph Johnson, dan John Vlissides — yang mendokumentasikan 23 pattern berdasarkan pengalaman bertahun-tahun membangun sistem nyata.
Yang penting dipahami sejak awal: pattern bukan potongan kode yang bisa di-copy-paste. Pattern adalah deskripsi tentang struktur, hubungan antar komponen, dan alur kolaborasi — sesuatu yang kemudian kamu implementasikan sesuai bahasa dan konteks proyekmu.
Pattern vs Framework vs Library #
Tiga konsep ini sering dicampuradukkan, padahal posisinya berbeda:
| Konsep | Apa Itu | Contoh |
|---|---|---|
| Design Pattern | Template solusi konseptual untuk masalah desain berulang | Singleton, Observer, Strategy |
| Framework | Kerangka aplikasi yang mendikte alur eksekusi; kamu mengisi “slot”-nya | Spring, Django, Laravel, Go Fiber |
| Library | Kumpulan fungsi/kelas yang kamu panggil sesuai kebutuhan | net/http, lodash, requests |
Framework sering mengimplementasikan pattern secara internal. Spring menggunakan Singleton untuk bean-nya. Angular menggunakan Observer untuk reactive data flow. Memahami pattern membantumu memahami mengapa framework dirancang seperti itu — bukan hanya cara menggunakannya.
Risiko Over-Engineering #
Pattern adalah alat, bukan tujuan. Salah satu jebakan paling umum adalah menerapkan pattern karena “terasa lebih profesional”, bukan karena ada masalah nyata yang dipecahkan.
flowchart TD
M[Menghadapi Masalah] --> A{Apakah masalah ini\nbenar-benar ada sekarang?}
A -- Tidak --> B[Tulis kode sederhana\nRefaktor nanti jika perlu]
A -- Ya --> C{Apakah ini masalah\nyang berulang?}
C -- Tidak --> D[Solusi ad-hoc\nyang tepat sasaran]
C -- Ya --> E{Apakah kompleksitas\npattern sebanding\ndengan manfaatnya?}
E -- Tidak --> D
E -- Ya --> F[Terapkan Pattern\nyang Sesuai]
“Premature optimization is the root of all evil” berlaku juga untuk pattern. Terapkan pattern saat masalahnya nyata — bukan untuk antisipasi masalah yang mungkin tidak pernah datang.
Peta Materi Pembelajaran #
Materi di website ini disusun dalam sembilan kelompok besar, dari fondasi paling dasar hingga keputusan arsitektur tingkat sistem. Setiap kelompok membangun pemahaman untuk kelompok berikutnya.
flowchart TD
subgraph Fondasi
A[1. Pengenalan\nDesign Pattern]
end
subgraph OOP["Pattern Berbasis Objek"]
B[2. Creational\nPatterns]
C[3. Structural\nPatterns]
D[4. Behavioral\nPatterns]
end
subgraph Sistem["Pattern Tingkat Sistem"]
E[5. Concurrency\nPatterns]
F[6. Data Access &\nMessaging Patterns]
G[7. Resource & Lifetime\nManagement]
end
subgraph Modern["Pattern Modern"]
H[8. Functional &\nModern Code Patterns]
end
subgraph Arsitektur["Arsitektur Software"]
I[9. Software\nArchitecture Patterns]
end
Fondasi --> OOP
OOP --> Sistem
Sistem --> Modern
Modern --> Arsitektur
Creational Patterns #
Creational patterns berfokus pada bagaimana objek dibuat — dengan cara yang terkontrol, fleksibel, dan tidak membebani dependensi sistem. Masalah yang sering muncul tanpa pattern ini: konstruksi objek tersebar di seluruh codebase, sulit diganti saat kebutuhan berubah, dan pengujian menjadi rumit karena dependensi hard-coded.
| Pattern | Masalah yang Dipecahkan |
|---|---|
| Singleton | Memastikan hanya ada satu instance untuk resource bersama (config, connection pool) |
| Factory Method | Memisahkan pembuatan objek dari penggunaannya agar jenis objek bisa diganti |
| Abstract Factory | Mengelompokkan pembuatan keluarga objek yang saling berkaitan |
| Builder | Menyederhanakan konstruksi objek kompleks dengan banyak parameter opsional |
| Prototype | Membuat objek baru dengan menyalin instance yang sudah ada |
Structural Patterns #
Structural patterns membantu menyusun hubungan antar objek dan komponen. Ketika dua sistem yang tidak kompatibel harus bekerja sama, atau ketika kamu ingin menambah fitur ke objek tanpa memodifikasi kode aslinya, structural patterns memberikan jawabannya.
| Pattern | Masalah yang Dipecahkan |
|---|---|
| Adapter | Menghubungkan dua interface yang tidak kompatibel |
| Bridge | Memisahkan abstraksi dan implementasi agar berkembang independen |
| Composite | Memperlakukan objek individual dan koleksi objek secara seragam |
| Decorator | Menambah perilaku ke objek secara dinamis tanpa memodifikasi kelas aslinya |
| Facade | Menyediakan interface sederhana di atas subsistem yang kompleks |
| Flyweight | Mengurangi penggunaan memori dengan berbagi data yang sama antar banyak objek |
| Proxy | Mengontrol akses ke objek untuk kebutuhan keamanan, caching, atau lazy loading |
Behavioral Patterns #
Behavioral patterns mengatur bagaimana objek berkomunikasi dan berbagi tanggung jawab. Banyak bug logika yang kompleks berakar dari komunikasi antar objek yang tidak terstruktur — behavioral patterns membantu membuatnya eksplisit dan dapat diprediksi.
| Pattern | Masalah yang Dipecahkan |
|---|---|
| Strategy | Mengganti algoritma secara dinamis tanpa mengubah kode klien |
| Observer | Menyebarkan perubahan state ke banyak objek secara loosely coupled |
| Command | Membungkus aksi sebagai objek untuk mendukung undo, logging, antrian |
| Chain of Responsibility | Mendistribusikan permintaan melalui rantai handler |
| State | Mengubah perilaku objek berdasarkan state internal tanpa if-else berlapis |
| Template Method | Menentukan kerangka algoritma dan menyerahkan detail ke subclass |
| Mediator | Memusatkan komunikasi antar objek untuk mengurangi coupling |
| Memento | Menyimpan dan memulihkan state objek tanpa melanggar enkapsulasi |
| Iterator | Menyediakan cara standar untuk melintasi koleksi tanpa ekspos strukturnya |
| Visitor | Menambah operasi ke struktur objek tanpa memodifikasi kelas-kelasnya |
| Interpreter | Mendefinisikan grammar untuk bahasa sederhana dan mengeksekusinya |
Concurrency Patterns #
Concurrency patterns membantu mengelola eksekusi paralel dengan aman dan efisien. Ini adalah salah satu area paling sulit dalam software engineering — bug concurrency bersifat non-deterministik, sulit direproduksi, dan bisa sangat merusak di production.
| Pattern | Masalah yang Dipecahkan |
|---|---|
| Thread Pool / Worker Pool | Mengontrol jumlah eksekusi paralel agar tidak membanjiri resource |
| Producer–Consumer | Memisahkan produksi dan konsumsi data dengan queue sebagai penyangga |
| Future / Promise | Merepresentasikan hasil komputasi async yang belum selesai |
| Async Callback | Menangani hasil operasi async tanpa memblokir thread |
| Fork–Join | Membagi tugas besar ke subtask paralel lalu menggabungkan hasilnya |
| Read–Write Lock | Mengoptimalkan akses concurrent dengan membedakan baca dan tulis |
| Double-Checked Locking | Menginisialisasi singleton secara lazy dan thread-safe |
| Immutable Object | Mencegah race condition dengan objek yang tidak bisa diubah |
| Guarded Suspension | Menunda eksekusi hingga kondisi prasyarat terpenuhi |
Data Access & Messaging Patterns #
Pattern di kelompok ini mengatur bagaimana data diakses, disimpan, dan dikomunikasikan antar komponen. Tanpa pattern yang tepat di sini, perubahan database atau messaging system bisa merambat ke seluruh codebase.
| Pattern | Masalah yang Dipecahkan |
|---|---|
| Repository | Abstraksi akses data yang memisahkan business logic dari storage |
| DAO | Mengisolasi detail persistence agar perubahan storage tidak memengaruhi domain |
| Unit of Work | Mengelola transaksi dan perubahan data secara terkoordinasi |
| Data Mapper | Memetakan data antara domain dan database tanpa mencemari model |
| Publish–Subscribe | Mengirim event ke banyak subscriber tanpa coupling langsung |
| Pipeline | Memproses data melalui tahapan berurutan yang modular |
Resource & Lifetime Management Patterns #
Pengelolaan lifecycle objek dan resource yang tidak tepat menyebabkan memory leak, koneksi yang tidak ditutup, dan inisialisasi yang lambat. Pattern di kelompok ini memberikan pola yang sistematis untuk masalah-masalah tersebut.
| Pattern | Masalah yang Dipecahkan |
|---|---|
| Object Pool | Mengurangi biaya pembuatan objek mahal dengan menggunakan ulang instance |
| Lazy Initialization | Menunda inisialisasi resource hingga benar-benar dibutuhkan |
| Reference Counting | Mengelola lifecycle objek berdasarkan jumlah referensi aktif |
| Dispose Pattern | Memastikan resource dilepaskan secara eksplisit dan aman |
Functional & Modern Code Patterns #
Paradigma fungsional membawa pola-pola baru yang semakin relevan di era bahasa modern seperti Go, Kotlin, Rust, dan TypeScript. Pattern di kelompok ini membantu menulis kode yang lebih komposabel, dapat diprediksi, dan bebas side effect.
| Pattern | Masalah yang Dipecahkan |
|---|---|
| Higher-Order Function | Mengoper fungsi sebagai data untuk meningkatkan komposabilitas |
| Function Composition | Menggabungkan fungsi kecil menjadi alur logika yang jelas |
| Monadic Pattern | Mengelola nilai opsional dan error secara eksplisit dan aman |
| Fluent Interface | Membuat API yang ekspresif dan mudah dibaca |
| Command Query Separation | Memisahkan operasi baca dan ubah state untuk kejelasan desain |
Software Architecture Patterns #
Architecture patterns beroperasi di level yang lebih tinggi dari design pattern — bukan tentang bagaimana satu kelas berinteraksi dengan kelas lain, melainkan tentang bagaimana seluruh sistem dibagi menjadi komponen-komponen besar dan bagaimana komponen tersebut berkomunikasi.
| Pattern | Masalah yang Dipecahkan |
|---|---|
| Layered Architecture | Memisahkan sistem ke lapisan tanggung jawab yang jelas |
| Clean Architecture | Menjaga business logic independen dari framework dan teknologi |
| Hexagonal Architecture | Domain sebagai pusat dengan port dan adapter sebagai batas |
| Onion Architecture | Dependensi berlapis yang semuanya mengarah ke domain |
| Domain-Driven Design | Memodelkan sistem berdasarkan bahasa dan kebutuhan bisnis |
| Modular Monolith | Monolith terstruktur dengan batas modul yang tegas |
| Microservices | Layanan kecil independen yang bisa dikembangkan dan di-deploy terpisah |
| Event-Driven Architecture | Event sebagai mekanisme utama komunikasi sistem |
| CQRS | Model baca dan tulis yang terpisah untuk skala dan kompleksitas tinggi |
| Serverless Architecture | Manajemen infrastruktur diserahkan ke platform cloud |
| MVC / MVP / MVVM | Pemisahan concern untuk aplikasi berbasis UI |
Siapa yang Cocok Membaca Website Ini? #
Website ini dirancang untuk rentang pembaca yang luas, tapi dengan kedalaman yang berbeda-beda tergantung latar belakang:
flowchart TD
subgraph Junior["Junior Engineer (1-2 tahun)"]
J["Fokus: Creational, Structural, Behavioral\nTujuan: Bangun kebiasaan desain yang baik sejak awal"]
end
subgraph Mid["Mid-Level Engineer (3-5 tahun)"]
M["Fokus: Concurrency, Data Access, Architecture dasar\nTujuan: Pahami trade-off dan kapan tidak menggunakan pattern"]
end
subgraph Senior["Senior / Tech Lead (5+ tahun)"]
S["Fokus: Architecture Patterns, DDD, trade-off skala\nTujuan: Gunakan sebagai referensi saat merancang sistem besar"]
end
Junior --> Mid --> Senior
Semua level bisa memulai dari bagian manapun yang relevan dengan masalah yang sedang dihadapi — tidak harus berurutan dari awal.
Cara Menggunakan Website Ini #
Ada dua cara membaca yang direkomendasikan, tergantung tujuanmu:
Baca berurutan jika kamu baru mengenal design pattern atau ingin membangun pemahaman yang sistematis. Mulai dari bagian Pengenalan, ikuti urutan dari Creational hingga Architecture. Setiap bagian membangun fondasi untuk bagian berikutnya.
Baca berdasarkan masalah jika kamu sudah berpengalaman dan sedang menghadapi masalah desain spesifik. Gunakan daftar pattern di atas sebagai referensi — identifikasi masalahmu, temukan pattern yang relevan, pelajari langsung ke bagian tersebut.
Yang perlu diingat saat membaca:
Cara membaca yang tepat:
✓ Baca penjelasan "mengapa" sebelum implementasi
✓ Perhatikan kapan pattern TIDAK tepat digunakan
✓ Hubungkan dengan masalah yang pernah kamu hadapi
✓ Gunakan sebagai referensi saat merancang, bukan checklist
Yang perlu dihindari:
✗ Menghapal nama pattern tanpa memahami konteksnya
✗ Menerapkan pattern karena "terasa profesional"
✗ Melewati bagian anti-pattern dan kapan tidak menggunakan
✗ Membaca tanpa mengaitkan dengan sistem nyata
Hubungan Antar Pattern #
Salah satu hal yang menarik dari design pattern adalah bahwa pattern-pattern ini tidak berdiri sendiri — banyak yang saling melengkapi, bahkan beberapa pattern sering digunakan bersama sebagai pasangan alami.
flowchart LR
subgraph Creational
Factory[Factory Method]
Builder
Singleton
end
subgraph Behavioral
Strategy
Observer
Command
end
subgraph Structural
Decorator
Proxy
Adapter
end
Factory -- "sering dipadukan" --> Strategy
Observer -- "digunakan bersama" --> Command
Proxy -- "dapat berperan sebagai" --> Decorator
Builder -- "membangun objek untuk" --> Strategy
Singleton -- "sering menjadi" --> Factory
Beberapa kombinasi yang sering ditemui di sistem nyata:
| Kombinasi | Konteks Umum |
|---|---|
| Factory + Strategy | Memilih dan membuat implementasi algoritma yang tepat saat runtime |
| Observer + Command | Event system di mana setiap event adalah Command yang bisa di-undo |
| Decorator + Proxy | Middleware chain yang menambah behavior sekaligus mengontrol akses |
| Builder + Singleton | Builder yang membuat satu-satunya instance objek konfigurasi |
| Repository + Unit of Work | Akses data yang transaksional dan konsisten |
Ringkasan #
- Design pattern adalah template solusi untuk masalah desain berulang — bukan kode siap pakai, melainkan pola struktur dan kolaborasi antar komponen.
- Mengapa sebelum bagaimana — memahami konteks kemunculan sebuah pattern jauh lebih penting dari menghapal implementasinya.
- Sembilan kelompok materi — Creational, Structural, Behavioral, Concurrency, Data Access & Messaging, Resource Management, Functional, dan Architecture Patterns — tersusun dari fondasi OOP hingga keputusan skala sistem.
- Pattern bukan tujuan — over-engineering dengan pattern yang tidak perlu lebih berbahaya dari tidak menggunakan pattern sama sekali. Terapkan saat masalahnya nyata.
- Pattern vs Framework vs Library — framework mengimplementasikan pattern secara internal; memahami pattern membantu memahami mengapa framework dirancang seperti itu.
- Pattern saling berkaitan — banyak pattern yang sering digunakan bersama sebagai pasangan alami: Factory + Strategy, Observer + Command, Repository + Unit of Work.
- Baca sesuai konteks — mulai berurutan jika baru mengenal pattern, atau langsung ke bagian yang relevan dengan masalah yang sedang dihadapi.
- Mental model adalah tujuan utama — yang paling berharga bukan daftar pattern yang dihapal, melainkan cara berpikirmu saat menghadapi masalah desain baru.
Mulai Belajar: Singleton →