S O L I D DRY KISS YAGNI

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.

Encapsulation
Abstraction
Inheritance
Polymorphism

OOP

Object-Oriented Programming

Four Principles ยท Infinite Possibilities

Organize code around objects โ€” each with its own data and behavior. The foundation for SOLID in WordPress: WP_Widget, WP_Query, WP_Post.

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.

Javascript
// 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.

Javascript
// 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.

Plaintext
// 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

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

Javascript
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

Javascript
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

Javascript
class QueryBuilder {
  select(fields) { this.query += fields; return this; }
  build() { return this.query; }
}
Prototype

Clone objects instead of creating new

Object copying, game entities

Javascript
class Sheep {
  clone() { return new Sheep(this.name); }
}

Structural

How objects are composed

Adapter

Make incompatible interfaces work together

Third-party library integration

Javascript
class PaymentAdapter {
  constructor(oldPayment) { this.old = oldPayment; }
  pay(amount) { this.old.oldPay(amount); }
}
Decorator

Add behavior to objects dynamically

Extend functionality without modifying code

Javascript
class MilkDecorator {
  constructor(coffee) { this.coffee = coffee; }
  cost() { return this.coffee.cost() + 2; }
}
Facade

Simplified interface to complex subsystem

Hide complexity, provide simple API

Javascript
class VideoConverter {
  convert(file, format) {
    const codec = new CodecFactory().get(format);
  }
}
Proxy

Placeholder for another object

Lazy loading, access control, logging

Javascript
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

Javascript
class Subject {
  notify(data) { this.observers.forEach(o => o.update(data)); }
}
Strategy

Select algorithm at runtime

Payment methods, sorting algorithms

Javascript
class ShoppingCart {
  constructor(strategy) { this.strategy = strategy; }
}
Command

Encapsulate request as an object

Undo/redo, macro recording, queuing

Javascript
class CommandManager {
  execute(cmd) { cmd.execute(); this.history.push(cmd); }
}
Iterator

Traverse collection without exposing structure

Custom collections, tree traversal

Javascript
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
<?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
<?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
<?php
$query = new WP_Query([
  'post_type' => 'post',
  'posts_per_page' => 10
]);

WP

Gutenberg Blocks

Factory Pattern

registerBlockType() creates different block types

Php
<?php
registerBlockType('my-plugin/custom-block', {
  title: 'Custom Block'
});

WP

Plugin Architecture

Dependency Inversion

Plugins depend on API abstractions, not concrete implementations

Php
<?php
add_filter('the_content', 'my_filter');
// Depends on abstraction (hooks)

WP

Widget System

Template Method

WP_Widget defines structure, subclasses implement details

Php
<?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
Plaintext
View  <--> Controller --> Model

MVVM

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
Plaintext
View <--> ViewModel <--> Model

Layered

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
Plaintext
Presentation > Business > Data Access > DB

Hexagonal

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
Plaintext
External > Adapter > Port > Core

Architecture 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