Design Patterns & Software Architecture #
Every engineer who has written code for a while has hit the same moments: looking back at code written six months ago and not understanding it. Or trying to add a small feature that turns out to require changes in ten different places. Or debating a design with teammates where everyone pictures a different solution to the same problem. These issues are not about technical skill — they are about lacking a shared language and framework for talking about code structure. Design patterns and software architecture fill that gap: not as magic recipes, but as vocabulary and a way of thinking that lets you design systems that last.
Goals of This Website #
Many design pattern resources jump straight to implementation code without explaining why a pattern exists and when it is the right fit. The result is that patterns get memorized rather than used as a thinking tool. This website takes a different approach.
Every topic is explored from its root problem: what problem gave rise to this pattern, how the naive solution fails, and why the pattern is the better answer. Implementation is present as proof of concept, not as the main goal.
There are four outcomes we want for every reader:
flowchart LR
A[Read\\nThis Website] --> B[Understand\\nWhy Patterns Exist]
B --> C[Recognize\\nWhen a Pattern Fits]
C --> D[Build a\\nDesign Mental Model]
D --> E[Design Systems\\nThat Last]Understand why — Patterns are not trivia. Every pattern was born from real experience dealing with a recurring problem. Understanding the context in which it emerged matters far more than memorizing its name.
Recognize when — The right pattern in the wrong situation is more dangerous than no pattern at all. You need to know when to use a pattern and when not to.
Build a mental model — The most valuable thing about learning design patterns is not the list of patterns you memorize, but how your thinking changes when you face a new design problem.
Design systems that last — Good code is not code that works today; it is code that can still be extended, tested, and understood six months from now.
What Is a Design Pattern? #
A design pattern is a solution template for recurring design problems in software development. The concept was popularized by the book Design Patterns: Elements of Reusable Object-Oriented Software (1994) by the Gang of Four — Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides — which documented 23 patterns drawn from years of experience building real systems.
One thing to understand from the start: a pattern is not a piece of copy-pasteable code. A pattern describes structure, relationships between components, and collaboration flows — something you then implement according to your language and project context.
Pattern vs Framework vs Library #
These three concepts are often mixed up, even though they occupy different positions:
| Concept | What It Is | Examples |
|---|---|---|
| Design Pattern | Conceptual solution template for recurring design problems | Singleton, Observer, Strategy |
| Framework | Application skeleton that dictates the execution flow; you fill in its “slots” | Spring, Django, Laravel, Go Fiber |
| Library | A collection of functions/classes you call as needed | net/http, lodash, requests |
Frameworks often implement patterns internally. Spring uses Singleton for its beans. Angular uses Observer for reactive data flow. Understanding patterns helps you understand why a framework is designed the way it is — not just how to use it.
The Over-Engineering Risk #
A pattern is a tool, not a goal. One of the most common traps is applying a pattern because it “feels more professional”, not because there is a real problem to solve.
flowchart TD
M[Facing a Problem] --> A{Does this problem\\nactually exist right now?}
A -- No --> B[Write simple code\\nRefactor later if needed]
A -- Yes --> C{Is this a\\nrecurring problem?}
C -- No --> D[Targeted ad-hoc\\nsolution]
C -- Yes --> E{Is the pattern's\\ncomplexity worth\\nthe benefit?}
E -- No --> D
E -- Yes --> F[Apply the\\nRight Pattern]“Premature optimization is the root of all evil” applies to patterns too. Apply a pattern when the problem is real — not in anticipation of problems that may never arrive.
Learning Roadmap #
The material on this website is organized into nine major groups, from the most basic foundations to system-level architecture decisions. Each group builds understanding for the next.
flowchart TD
subgraph Fondasi[Foundations]
A[1. Introduction\\nto Design Patterns]
end
subgraph OOP["Object-Based Patterns"]
B[2. Creational\\nPatterns]
C[3. Structural\\nPatterns]
D[4. Behavioral\\nPatterns]
end
subgraph Sistem["System-Level Patterns"]
E[5. Concurrency\\nPatterns]
F[6. Data Access &\\nMessaging Patterns]
G[7. Resource & Lifetime\\nManagement]
end
subgraph Modern["Modern Patterns"]
H[8. Functional &\\nModern Code Patterns]
end
subgraph Arsitektur["Software Architecture"]
I[9. Software\\nArchitecture Patterns]
end
Fondasi --> OOP
OOP --> Sistem
Sistem --> Modern
Modern --> ArsitekturCreational Patterns #
Creational patterns focus on how objects are created — in a controlled, flexible way that does not burden the system’s dependencies. The problems that appear without these patterns: object construction scattered across the codebase, hard to swap when requirements change, and complicated testing due to hard-coded dependencies.
| Pattern | Problem It Solves |
|---|---|
| Singleton | Ensures only one instance exists for a shared resource (config, connection pool) |
| Factory Method | Separates object creation from its use so the object type can be swapped |
| Abstract Factory | Groups the creation of related object families |
| Builder | Simplifies constructing complex objects with many optional parameters |
| Prototype | Creates new objects by copying an existing instance |
Structural Patterns #
Structural patterns help organize relationships between objects and components. When two incompatible systems must work together, or when you want to add features to an object without modifying its original code, structural patterns provide the answer.
| Pattern | Problem It Solves |
|---|---|
| Adapter | Connects two incompatible interfaces |
| Bridge | Separates abstraction from implementation so each can evolve independently |
| Composite | Treats individual objects and collections of objects uniformly |
| Decorator | Adds behavior to objects dynamically without modifying the original class |
| Facade | Provides a simple interface on top of a complex subsystem |
| Flyweight | Reduces memory usage by sharing identical data across many objects |
| Proxy | Controls access to an object for security, caching, or lazy loading |
Behavioral Patterns #
Behavioral patterns govern how objects communicate and share responsibility. Many complex logic bugs are rooted in unstructured inter-object communication — behavioral patterns help make it explicit and predictable.
| Pattern | Problem It Solves |
|---|---|
| Strategy | Swaps algorithms dynamically without changing client code |
| Observer | Spreads state changes to many objects in a loosely coupled way |
| Command | Wraps an action as an object to support undo, logging, queuing |
| Chain of Responsibility | Distributes requests through a chain of handlers |
| State | Changes an object’s behavior based on internal state without nested if-else |
| Template Method | Defines an algorithm skeleton and delegates details to subclasses |
| Mediator | Centralizes communication between objects to reduce coupling |
| Memento | Saves and restores an object’s state without breaking encapsulation |
| Iterator | Provides a standard way to traverse collections without exposing their structure |
| Visitor | Adds operations to an object structure without modifying its classes |
| Interpreter | Defines a grammar for a simple language and executes it |
Concurrency Patterns #
Concurrency patterns help manage parallel execution safely and efficiently. This is one of the hardest areas in software engineering — concurrency bugs are non-deterministic, hard to reproduce, and potentially very damaging in production.
| Pattern | Problem It Solves |
|---|---|
| Thread Pool / Worker Pool | Controls the amount of parallel execution so resources are not overwhelmed |
| Producer–Consumer | Separates data production from consumption with a queue as buffer |
| Future / Promise | Represents the result of an async computation that has not finished yet |
| Async Callback | Handles async operation results without blocking the thread |
| Fork–Join | Splits a large task into parallel subtasks and merges the results |
| Read–Write Lock | Optimizes concurrent access by distinguishing reads from writes |
| Double-Checked Locking | Initializes a singleton lazily and thread-safely |
| Immutable Object | Prevents race conditions with objects that cannot be changed |
| Guarded Suspension | Delays execution until a precondition is met |
Software Architecture Patterns #
Architecture patterns operate at a higher level than design patterns — not about how one class interacts with another, but about how the whole system is divided into large components and how those components communicate.
| Pattern | Problem It Solves |
|---|---|
| Layered Architecture | Separates the system into clear layers of responsibility |
| Clean Architecture | Keeps business logic independent of frameworks and technologies |
| Hexagonal Architecture | Domain at the center with ports and adapters as the boundary |
| Onion Architecture | Layered dependencies that all point toward the domain |
| Domain-Driven Design | Models the system based on business language and needs |
| Modular Monolith | A structured monolith with firm module boundaries |
| Microservices | Small independent services that can be developed and deployed separately |
| Event-Driven Architecture | Events as the primary communication mechanism of the system |
| CQRS | Separate read and write models for high scale and complexity |
| Serverless Architecture | Infrastructure management handed over to the cloud platform |
| MVC / MVP / MVVM | Separation of concerns for UI-based applications |
Who Is This Website For? #
This website is designed for a broad range of readers, but with different depths depending on your background:
flowchart TD
subgraph Junior["Junior Engineer (1-2 years)"]
J["Focus: Creational, Structural, Behavioral\\nGoal: Build good design habits from day one"]
end
subgraph Mid["Mid-Level Engineer (3-5 years)"]
M["Focus: Concurrency, Data Access, basic Architecture\\nGoal: Understand trade-offs and when NOT to use a pattern"]
end
subgraph Senior["Senior / Tech Lead (5+ years)"]
S["Focus: Architecture Patterns, DDD, scale trade-offs\\nGoal: Use as a reference when designing large systems"]
end
Junior --> Mid --> SeniorEvery level can start from whichever section is relevant to the problem at hand — there is no need to go through everything in order.
How to Use This Website #
There are two recommended ways to read, depending on your goal:
Read sequentially if you are new to design patterns or want to build a systematic understanding. Start from the Introduction and follow the order from Creational to Architecture. Each section builds the foundation for the next.
Read by problem if you are experienced and facing a specific design problem. Use the pattern lists above as a reference — identify your problem, find the relevant pattern, and jump straight to that section.
Things to keep in mind while reading:
How to read properly:
✓ Read the "why" explanation before the implementation
✓ Note when a pattern is NOT the right fit
✓ Connect it with problems you have faced before
✓ Use it as a reference while designing, not as a checklist
What to avoid:
✗ Memorizing pattern names without understanding the context
✗ Applying a pattern because it "feels professional"
✗ Skipping the anti-pattern sections and when-not-to-use guidance
✗ Reading without connecting it to real systems
Relationships Between Patterns #
One of the interesting things about design patterns is that they do not stand alone — many complement each other, and some are frequently used together as natural pairs.
flowchart LR
subgraph Creational
Factory[Factory Method]
Builder
Singleton
end
subgraph Behavioral
Strategy
Observer
Command
end
subgraph Structural
Decorator
Proxy
Adapter
end
Factory -- "often paired with" --> Strategy
Observer -- "used together with" --> Command
Proxy -- "can act as" --> Decorator
Builder -- "builds objects for" --> Strategy
Singleton -- "often becomes" --> FactorySome combinations frequently found in real systems:
| Combination | Common Context |
|---|---|
| Factory + Strategy | Selecting and creating the right algorithm implementation at runtime |
| Observer + Command | Event systems where every event is an undoable Command |
| Decorator + Proxy | Middleware chains that add behavior while controlling access |
| Builder + Singleton | A builder creating the single config object instance |
| Repository + Unit of Work | Transactional, consistent data access |
Summary #
- A design pattern is a solution template for recurring design problems — not ready-made code, but a pattern of structure and collaboration between components.
- Why before how — understanding the context in which a pattern emerged matters far more than memorizing its implementation.
- Nine material groups — Creational, Structural, Behavioral, Concurrency, Data Access & Messaging, Resource Management, Functional, and Architecture Patterns — arranged from OOP foundations to system-scale decisions.
- A pattern is not a goal — over-engineering with an unnecessary pattern is more dangerous than using none at all. Apply it when the problem is real.
- Pattern vs Framework vs Library — frameworks implement patterns internally; understanding patterns helps you understand why a framework is designed the way it is.
- Patterns are interrelated — many are used together as natural pairs: Factory + Strategy, Observer + Command, Repository + Unit of Work.
- Read according to context — go sequentially if you are new to patterns, or jump straight to the section relevant to the problem you are facing.
- The mental model is the real goal — the most valuable thing is not the list of patterns you memorize, but how your thinking changes when facing new design problems.
Next: Singleton →