Software Architecture
Structure WordPress plugins with intent โ start with OOP, then SOLID, design patterns, and architectural best practices for code you can extend without rewriting.
Real patterns from 4WP plugins โ not textbook theory. Architecture explains how to structure code; Development covers WP API mechanics.
Essential Principles
Fundamental rules that every developer should follow
DRY
Donโt Repeat Yourself
Every piece of knowledge must have a single, unambiguous representation in the system.
// Good โ
function getUserFullName() {
return user.firstName + ' ' + user.lastName;
}KISS
Keep It Simple, Stupid
Most systems work best if they are kept simple rather than made complex.
// Good โ
const result = arr
.filter(val => val > 5)
.map(val => val * 2);YAGNI
You Arenโt Gonna Need It
Donโt add functionality until itโs necessary. Avoid over-engineering.
// Good โ
class User {
login() {}
logout() {}
}SOLID Principles in WordPress
Five fundamental principles for object-oriented design
Single Responsibility
One class โ one responsibility
Open/Closed
Open for extension, closed for modification
Liskov Substitution
Subtypes must be substitutable
Interface Segregation
Many specific interfaces better than one
D
Dependency Inversion
Depend on abstractions, not concretions
Gang of Four Patterns
23 battle-tested patterns from the Gang of Four โ the shared vocabulary for structuring WordPress plugins.
Expand any pattern for a quick definition, real use case, and a minimal code sketch. Filters below group patterns by intent โ not every GoF pattern appears here; these are the ones you will reach for most often.
All Patterns
Creational
Structural
Behavioral
Creational
Object creation mechanisms
Singleton
Ensure a class has only one instance
Database connections, configuration managers
class Database {
static instance;
static getInstance() {
if (!Database.instance) {
Database.instance = new Database();
}
return Database.instance;
}
}Factory
Create objects without specifying exact class
UI components, document parsers
class ButtonFactory {
createButton(os) {
if (os === 'Windows') return new WindowsButton();
if (os === 'Mac') return new MacButton();
}
}Builder
Construct complex objects step by step
Complex object creation, query builders
class QueryBuilder {
select(fields) { this.query += fields; return this; }
build() { return this.query; }
}Prototype
Clone objects instead of creating new
Object copying, game entities
class Sheep {
clone() { return new Sheep(this.name); }
}Structural
How objects are composed
Adapter
Make incompatible interfaces work together
Third-party library integration
class PaymentAdapter {
constructor(oldPayment) { this.old = oldPayment; }
pay(amount) { this.old.oldPay(amount); }
}Decorator
Add behavior to objects dynamically
Extend functionality without modifying code
class MilkDecorator {
constructor(coffee) { this.coffee = coffee; }
cost() { return this.coffee.cost() + 2; }
}Facade
Simplified interface to complex subsystem
Hide complexity, provide simple API
class VideoConverter {
convert(file, format) {
const codec = new CodecFactory().get(format);
}
}Proxy
Placeholder for another object
Lazy loading, access control, logging
class ProxyImage {
display() {
if (!this.real) this.real = new RealImage();
}
}Behavioral
Communication between objects
Observer
Subscribe to events and get notifications
Event handling, pub/sub systems
class Subject {
notify(data) { this.observers.forEach(o => o.update(data)); }
}Strategy
Select algorithm at runtime
Payment methods, sorting algorithms
class ShoppingCart {
constructor(strategy) { this.strategy = strategy; }
}Command
Encapsulate request as an object
Undo/redo, macro recording, queuing
class CommandManager {
execute(cmd) { cmd.execute(); this.history.push(cmd); }
}Iterator
Traverse collection without exposing structure
Custom collections, tree traversal
class BookIterator {
hasNext() { return this.index < this.books.length; }
}Real-World Practice
GoF patterns are abstract โ WordPress makes them concrete.
Every example below maps a core WP API to a named pattern. If you already use hooks, gateways, or WP_Query, you are applying these patterns โ now with names attached.
WP
Hooks System
Observer Pattern
add_action() and add_filter() register observers on a subject โ WordPress fires the hook, every callback reacts.
<?php
add_action('save_post', function($post_id) {
notify_users($post_id);
update_cache($post_id);
});WP
Woo Payment Gateways
Strategy Pattern
Different payment strategies selected at runtime
<?php
class WC_Gateway_Stripe extends WC_Payment_Gateway {
public function process_payment($order_id) {}
}WP
WP_Query
Facade Pattern
Simple API that hides complex database operations
<?php
$query = new WP_Query([
'post_type' => 'post',
'posts_per_page' => 10
]);WP
Gutenberg Blocks
Factory Pattern
registerBlockType() creates different block types
<?php
registerBlockType('my-plugin/custom-block', {
title: 'Custom Block'
});WP
Plugin Architecture
Dependency Inversion
Plugins depend on API abstractions, not concrete implementations
<?php
add_filter('the_content', 'my_filter');
// Depends on abstraction (hooks)WP
Widget System
Template Method
WP_Widget defines structure, subclasses implement details
<?php
class My_Widget extends WP_Widget {
public function widget($args, $instance) {}
}Architectural Patterns
High-level patterns for structuring entire applications โ not single classes, but how layers talk to each other.
Pick the pattern that matches your pluginโs complexity. MVC fits admin screens and REST endpoints; Layered and Hexagonal shine when you need testable cores and swappable infrastructure.
MVC
Model-View-Controller
Separates data, presentation, and request handling โ the default mental model for WordPress admin pages and REST controllers.
Components:
Model: Data & Logic
View: UI Presentation
Controller: Input
Benefits:
- Clear separation
- Parallel development
- Multiple views
- Easy to modify
View <--> Controller --> ModelMVVM
Model-View-ViewModel
Separates UI from business logic with data binding โ common in React block editors and SPA-style admin tools.
Components:
Model: Data
View: UI
ViewModel: Logic
Benefits:
- Data binding
- Testability
- Separation of concerns
- Reusable ViewModels
View <--> ViewModel <--> ModelLayered
Layered Architecture
Organizes code into horizontal layers โ presentation, domain, data โ each depending only on the layer below.
Components:
- Presentation Layer
- Business Layer
- Data Access Layer
- Database
Benefits:
- Easy to understand
- Maintainable
- Testable
- Reusable layers
Presentation > Business > Data Access > DBHexagonal
Ports & Adapters
Keeps the domain core independent of WordPress, database, and external APIs โ swap adapters without rewriting business rules.
Components:
- Core Domain
- Ports (Interfaces)
- Adapters
- External Systems
Benefits:
- Testability
- Flexibility
- Independence
- Swappable dependencies
External > Adapter > Port > CoreArchitecture Best Practices
Apply these principles consistently to build robust, maintainable, and scalable applications
Modularity
Break down complex systems into smaller, manageable modules
Encapsulation
Hide internal details and expose only necessary interfaces
Abstraction
Work with high-level concepts rather than low-level details
Loose Coupling
Minimize dependencies between components for flexibility