Domain-Driven Design in PHP: How Not to Drown in Complexity

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Domain-Driven Design in PHP: How to Turn Chaos into Order

\n\n

Have you ever felt that your PHP project is growing like a snowball? At first, everything is simple: a couple of controllers, a model, a few database queries. And six months later, you're drowning in spaghetti code, where business logic is smeared across services, and a "simple" requirement change takes a week.

\n\n

Sound familiar? Welcome to the club. But there's good news: Domain-Driven Design (DDD) is not an abstract theory from Eric Evans' thick books. It's a working tool that really helps you write PHP code that is understandable to both business and developers.

\n\n

In this article, we'll break down DDD in practice. No fluff, with live examples in PHP. You'll learn how to implement DDD in your project without breaking your head or rewriting everything from scratch. Let's go!

\n\n

What is Domain-Driven Design and Why Does a PHP Developer Need It?

\n\n

Imagine you're writing an online store. You have discounts, a cart, orders. Business logic: "discount does not stack with promo code," "order cannot be canceled after shipment." If you store this logic in controllers or spread it across ORM models, you will eventually get confused.

\n\n

DDD is an approach that puts the business domain (your subject area) at the center of development. Instead of thinking about tables and SQL queries, you think about entities, values, and events.

\n\n

This is especially relevant for PHP. The language is flexible, but without a clear architecture, projects quickly turn into "legacy." DDD provides a framework that remains readable a year later.

\n\n

Strategic vs. Tactical Design

\n\n

DDD is divided into two large parts:

\n
    \n
  • Strategic Design — this is the map of your project. You identify Bounded Contexts, build a Ubiquitous Language. For example, in the "Catalog" context, a product is just a set of characteristics, but in the "Order" context, it's a unit with a price and quantity.
  • \n
  • Tactical Design — these are the building blocks: Entity, Value Object, Aggregate, Repository, Domain Service, Domain Event. We'll focus on these.
  • \n
\n\n

Tactical Design in PHP: DDD Building Blocks

\n\n

Let's look at how the main DDD patterns appear in PHP. Forget about ActiveRecord from Laravel or Symfony — here we build a model independent of the framework.

\n\n

1. Entity

\n

An Entity is an object that has a unique identifier and changes over time. For example, User or Order. We compare entities by ID, not by fields.

\n\n
<?php\n\ndeclare(strict_types=1);\n\nnamespace App\\Domain\\Entity;\n\nuse App\\Domain\\ValueObject\\UserId;\nuse App\\Domain\\ValueObject\\Email;\n\nclass User\n{\n    public function __construct(\n        private UserId $id,\n        private string $name,\n        private Email $email\n    ) {}\n\n    public function getId(): UserId\n    {\n        return $this->id;\n    }\n\n    public function changeEmail(Email $newEmail): void\n    {\n        // Business logic can go here: check if email is not taken\n        $this->email = $newEmail;\n    }\n\n    public function getEmail(): Email\n    {\n        return $this->email;\n    }\n}\n
\n\n

2. Value Object

\n

A Value Object is an immutable object defined by its attributes, not an identifier. Two Value Objects are equal if all their fields match. Classics: Money, Email, Address.

\n\n
<?php\n\ndeclare(strict_types=1);\n\nnamespace App\\Domain\\ValueObject;\n\nclass Email\n{\n    private string $value;\n\n    public function __construct(string $value)\n    {\n        if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {\n            throw new \\InvalidArgumentException('Invalid email address');\n        }\n        $thi                

Blogs

Book Recommendations