A 21-lesson course in modelling business rules in PHP 8.5 — aggregates, invariants, repositories, typed failure trees — and, first, how to tell when none of it is worth the ceremony.
Built as the sequel to PHP 8.5 OOP Mastery. Every reference solution is executed against its own assertions on every verification run, and every earlier lesson's specification is re-run against the newest code. Free, MIT licensed, and complete.
Every lesson comes back to it.
“If I deleted my framework, my database driver and my web server tomorrow morning, would the code in this file still make sense to someone who runs the business?”
Module 0 is two lessons, no code, and its position is that most applications should
not use tactical DDD — applied to a CRUD app it produces four layers of indirection
around an UPDATE statement. Learning to tell the difference is the more valuable
half, and it is the half most material on the subject omits.
A vehicle workshop, built up from the first lesson to the last. The capstone assembles classes first written in Module 1 without changing one of them — which is the real test of whether the earlier modules got it right.
Pricing the ceremony. The signals that justify it, and the ones that do not.
Rich versus anemic models, value objects, entities and identity, and where invariants live.
What must be true together, right now — and why the boundary is smaller than you think.
Repository interfaces the domain declares, reconstitution, contract tests run against two implementations.
Why APIs return 500 for user error, and the typed failure tree that fixes it — ending at an RFC 9457 boundary.
The situation almost everyone is actually in: the ORM owns your model and rewriting is not on the table. A costed ladder of what transfers, then strangling one aggregate out of a legacy service — with a characterisation test pinning down what the old code actually does before it is replaced.
The whole domain behind a Slim 4 API, wired with PHP-DI, plus a test that scans every import in the domain layer.
The capstone reads every file in the domain layer with PHP's tokeniser and records every type it names. The result is a build failure if it ever stops being true.
87 files scanned. Every name they refer to, in full:
DomainException InvalidArgumentException NoDiscard Throwable
Not “no framework imports” — no imports. Domain classes share a namespace, so they name each other unqualified; the only names that survive are the SPL types the exception tree extends. Delete Slim, PDO, PHP-DI and the web server, and there is nothing left to remove.
RUN-THIS-FIRST.md, with what each one cost.
MIT — the writing as well as the code. Use it, change it, teach from it, translate it, sell it, build your own paid course on top of it. The only obligation is keeping the copyright notice.