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
phpenum PatchState { case Missing; }
Use in DTO
phpreadonly class UserUpdateDto { public function __construct( public string|null|PatchState $firstName = PatchState::Missing, ) {} }
Entity Logic
phppublic 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.