5.1 · Design patterns, clean architecture & SOLID

Level 5 · Pro level & deployment

5.1Design patterns, clean architecture & SOLID

Goal: design cleaner code (design patterns, SOLID principles, architecture) with judgment — the step toward expertise: robust, maintainable, evolvable code, not just working code.
Estimated time: 12 min · self-paced reading · If you only have 5 min: read “The essentials in 30s” + the diagram.

The essentials in 30 seconds

  • Patterns = proven solutions: validated organisation recipes (Factory, Repository, dependency injection) + a shared vocabulary.
  • SOLID = 5 principles: single responsibility, open/closed, Liskov, interface segregation, dependency inversion.
  • Judgment: apply with measure according to the problem — neither over-engineering nor mess.

Running case — Riad structures his shop in Constantine

Riad, a web developer in Constantine, maintains a small online shop: the code mixes SQL queries, HTML and pricing rules in the same files, and every fix breaks something else. He centralises data access in a Repository, injects his dependencies instead of creating them hard-coded, and splits a class that did everything. This section tells his switch to code designed to last — and the thread continues in 5.2, 5.3 and 5.4.

This same case returns in the lesson, the diagram, the script and the 48h challenge — and continues in 5.2, 5.3 and 5.4.

1. Design patterns

At expert level, the question becomes “how to structure code so it is robust, maintainable, evolvable”. Design patterns are proven solutions to recurring design problems: organisation recipes validated by the community, which avoid reinventing the wheel. Common in PHP: the Factory (delegating object creation to decouple), the Repository (centralising data access for one entity), the Strategy (interchangeable algorithms), the Observer (notifying of an event), dependency injection (supplying an object with its dependencies from the outside — central in modern frameworks). Riad rediscovers in them his everyday problems already solved by others.

Decisive bonus: patterns provide a shared vocabulary — saying “it's a Repository” communicates a design intention. And frameworks (Laravel, Symfony) use them massively: recognising them means understanding what the framework does instead of enduring it.

2. The SOLID principles

SOLID is five object-oriented design principles: Single Responsibility (one class = one single responsibility, one single reason to change), Open/Closed (open for extension, closed for modification: add without breaking the existing), Liskov Substitution (a derived class must be able to replace its parent without breaking behaviour), Interface Segregation (several specific interfaces rather than one big general interface), Dependency Inversion (depend on abstractions — interfaces — rather than on concrete implementations). Applied, they produce decoupled, extensible, testable code. Riad starts with S and D: splitting his catch-all class, then depending on an interface.

Clean architecture extends this thinking: layer separation, decoupling, readable code. Code that follows these principles is easy to test — and to evolve without fear.

3. Judgment above all

Essential point of perspective: patterns and SOLID apply with judgment, not dogmatically. Over-engineering (complex patterns and abstraction layers where a simple solution would do) is as real a trap as under-engineering (messy code): useless complexity, harder-to-understand code, paradoxical rigidity, wasted time. Riad does not add three layers to a one-page script — he structures what grows and lasts.

These concepts are also transversal: they hold in all object-oriented programming, beyond PHP. Mastering them marks the step toward expertise: no longer just making code work, but designing it to last — a skill acquired through practice and reading good code, notably that of frameworks.

Common misinterpretation: “To be pro, you must apply patterns everywhere and follow SOLID to the letter”. No. Patterns are solutions to specific problems — without the problem, the pattern is dead weight. And SOLID guides, it does not dictate: a class with two stable, tiny responsibilities does not necessarily deserve to be split. The right reflex: start from the problem (maintenance pain, impossible test, duplication), then choose the tool — never the reverse.
Designing code that lasts Problem first, structure second PatternsProven solutions (Factory, Repository, injection)+SOLID5 principles (S and D first)+JudgmentThe right level, neither too much nor too little Read good code (frameworks) to see patterns in real situations.
Problem first, structure second
Text version of the diagram (SEO / screen readers / translation): 1) Patterns: Riad centralises data access for his shop in Constantine in a Repository and injects his dependencies. 2) SOLID: he splits his catch-all class (single responsibility) and depends on interfaces (dependency inversion). 3) Judgment: he only over-structures what grows — a throwaway script stays simple. Lesson: design for maintenance and testing, with measure.

🔎 Concrete example — Riad centralises data access

A testable Repository, injected dependency.

pdo->prepare('SELECT * FROM produits WHERE id = :id');
        $req->execute(['id' => $id]);
        $ligne = $req->fetch(PDO::FETCH_ASSOC);
        return $ligne === false ? null : $ligne;
    }
}
// The controller receives the repository (injection), it does not create it:
// $controleur = new ProduitControleur(new ProduitRepository($pdo));

Reading: the SQL lives in one place, the controller depends on an interface — it can be tested with a fake repository.

Lesson: Repository + injection = decoupled, testable code that evolves without breakage.

Section vocabulary

Design patterns
Proven solutions to recurring design problems, with a shared vocabulary.

Simple example: Riad centralises his product queries in a Repository.

What it is not: mandatory rules to apply everywhere.

Reflex: which recurring problem am I solving?

Repository
Pattern that centralises data access for one entity in a single place.

Simple example: All of Riad's product queries go through ProduitRepository.

What it is not: SQL scattered across views and controllers.

Reflex: is my SQL grouped and reusable?

Dependency injection
Supplying an object with its dependencies from the outside instead of it creating them.

Simple example: Riad's controller receives the PDO instead of building it.

What it is not: a new PDO hidden deep inside a method.

Reflex: can I test this class with a fake?

SOLID
Five OO design principles toward decoupled, maintainable code.

Simple example: Riad splits his catch-all class (S) then depends on interfaces (D).

What it is not: a check-list to tick blindly.

Reflex: which SOLID letter relieves my current pain?

Over-engineering
Too much structure and abstraction where a simple solution would do.

Simple example: Riad leaves his one-page script simple, with no layers.

What it is not: messy code with no structure at all.

Reflex: does this abstraction pay for its cost?

In practice — Designing clean, maintainable code

  1. Discover a few common design patterns (Factory, Repository, dependency injection) and spot them in the code of a framework (Laravel/Symfony).
  2. Apply Single Responsibility (the S of SOLID): one class = one responsibility; split a class that does too much.
  3. Apply Dependency Inversion (D): make your code depend on an INTERFACE rather than a concrete class (more flexible, testable).
  4. Exercise JUDGMENT: apply the structure suited to the problem, with no over-engineering (neither needless complex patterns nor mess).
You design cleaner code (design patterns, SOLID principles, architecture) with judgment — the step toward expertise: designing robust, maintainable, evolvable code, not just working code.

Ready-to-say script — proposing a clean split

“On our shop in Constantine, SQL queries are mixed with HTML and every fix breaks something else. I suggest centralising data access in a Repository and injecting dependencies: we will be able to test without the database and change the display without touching the data. I start with one class, we measure the gain, then we extend.”

Say in code review to get a gradual split adopted, without promising a complete rewrite.

Copy-paste template — SOLID review grid

SOLID REVIEW (1 class)
S — Single responsibility: does this class have ONE single reason to change? → If not: what to split? ___
O — Open/Closed: to add a case, must I MODIFY the existing? → If yes: which extension to plan? ___
L — Liskov: can a child class replace its parent? → Test: ___
I — Interfaces: is the interface too big? → To split into: ___
D — Inversion: what CONCRETE thing does it depend on? → Replace with the interface: ___

Solo / small budget

Riad alone: applying S and D on the two most painful classes is enough — no need to restructure everything.

Small business / small team

One shared Repository for the critical entities: the whole team reads and writes data in the same place.

SME in Constantine

Riad schedules a monthly SOLID review with the team in Constantine: one class passed through the grid each time.

Starting team

Pair: one codes the feature, the other reviews with the grid; a pattern is only added for a real problem.

✓ Best practices

  • Start with S (single responsibility) and D (dependency inversion): the best effort/gain ratio.
  • Read good code (frameworks) to see patterns in real situations.
  • Only add a pattern for a real maintenance or testing problem.

✗ Common pitfalls

  • Applying patterns everywhere “to be pro” (over-engineering).
  • Abusing the Singleton (hidden global state, hard tests) — to be used sparingly.
  • Confusing architecture with paperwork: layers that add nothing cost understanding.

Key takeaways

  • DESIGN PATTERNS = PROVEN solutions to recurring design problems (Factory, Repository, Strategy, Observer, dependency injection); shared vocabulary. Frameworks use them massively.
  • SOLID = 5 OO design principles: Single Responsibility (one responsibility), Open/Closed (extension without modification), Liskov, Interface Segregation, Dependency Inversion (depending on ABSTRACTIONS).
  • Applied, they produce DECOUPLED, extensible, testable, maintainable code (clean architecture: separated layers, decoupling, readability).
  • JUDGMENT: apply with MEASURE according to the problem — OVER-ENGINEERING (excessive structure) is a trap just like mess. TRANSVERSAL concepts (beyond PHP). Marks the step toward EXPERTISE (designing, not just making work).

48h challenge — apply tomorrow

Pass 1 class of your project through the review grid above: which SOLID letter hurts you most — and which split do you suggest?

Section exit checklist
  • I can cite 3 patterns and their use in one sentence each.
  • My reviewed class follows S and D (or I know what to split).
  • I know when NOT to add a pattern.
Self-rating 1-5: does my code test without the database or HTML?did I add structure only where it relieves pain?

Frequently asked questions

Should you learn design patterns and SOLID, or is it superfluous theory?

Neither superfluous nor to be learned in one go: they are proven design tools that pay off on projects that last. Patterns save you from reinventing (badly) validated solutions, SOLID guides toward maintainable code, and both provide a shared vocabulary precious in a team. Above all, frameworks use these patterns: knowing them means understanding Laravel and Symfony instead of imitating them. But they are learned gradually (one pattern at a time, S and D first), make sense with experience, and apply with judgment: a small script needs no architecture. Start by spotting patterns in good code, apply them on your real pains — theory will follow practice.

What is over-engineering, and how to find the right level of structure?

Over-engineering is designing more complex than the problem requires: layers, interfaces and patterns where a simple solution would do. Real costs: needless complexity (hard to understand), more code to maintain, paradoxical rigidity, wasted time. The classic trap: excited after learning patterns, you want to apply them everywhere. To find the right level: start from the real pain (impossible test? duplication? changes that break everything?) and choose the minimal tool that relieves it; leave code simple while it does not hurt; review with the SOLID grid to check, not to tick. Neither a cathedral for a hut, nor a hut for a cathedral.

Check your understanding

What are design patterns and what is SOLID for?

Additional resources (optional, to go further)

Level 5 examFresh, harder questions to test what you've learned