2025-12-07
#architecture
#backend
#ddd

DDD & Rich Model

Rejecting Anemic Models in favor of Rich Entities and Strict Patch Updates.

My architecture is based on the Rich Domain Model principle. I reject "Anemic" models (Anemic Model), which are merely data containers, in favor of entities that encapsulate behavior.

Core Principles

1. Rich Entities

Entities manage their own state. Setters (setStatus) are replaced by action methods (cancel(), activate()) that protect invariants.

Bad (Anemic):

php
$order->status = 'cancelled';
$order->save();

Good (Rich):

php
// Inside Order::cancel() rules are checked:
// - Can it be cancelled?
// - Has the time passed?
$order->cancel(); 
$repository->save($order);

2. Strict Patch Updates (Partial Updates)

To implement PATCH requests, I use the Strict Patch State pattern to distinguish between "field not passed" and "field passed as null".

PatchState Enum

php
enum PatchState
{
    case Missing;
}

Use in DTO

php
readonly class UserUpdateDto
{
    public function __construct(
        public string|null|PatchState $firstName = PatchState::Missing,
    ) {}
}

Entity Logic

php
public function update(UserUpdateDto $dto): void
{
    if ($dto->firstName !== PatchState::Missing) {
        $this->first_name = $dto->firstName;
    }
}

3. Factories & Named Constructors

Creation of complex aggregates is encapsulated in Factories or Named Constructors.

  • Named Constructor (Entity::register(...)): Creates an entity from primitives with a valid initial state.
  • Factory: Loads data from external sources (DTO, API) and maps it to entity constructor arguments.

Connected Thoughts

Egor Zdioruc | Lead Full Stack Developer | Laravel & AI Solutions