O U T T H E D O M A I N ❓ What is a Subscription in my domain ❓ What is a Price in my domain ❓ What does it mean to be a promotional subscription ❓ What does it mean to change a subscription’s price
S O F D D D WORD DESCRIPTION Entity Unique object with an identity Value Object Data with no identity, defined by attributes Aggregate Group of related entities treated as a single unit Relationship How content entities connect Bounded Context Defines the limits of a domain
S 1 2 3 4 5 6 7 8 final readonly class SubscriptionId { public function __construct( public int $value, ) { Assert::positiveInteger($id, 'Subscription ID must be a positive integer'); } } Requires webmozart/assert library. Assert::xxx throws an \InvalidArgumentException if the condition is not met.
10 11 12 13 14 final readonly class Subscription { public function __construct( public SubscriptionId $id, public Price $price, private bool $isPromotion = false, ) { } public function withPrice(Price $price): self { return new self($this->id, $this->name, $price, $this->isPromotion); } }
10 11 12 13 14 15 final readonly class VatHandler { /** * Germany's VAT for our Subscriptions is 19% * Subscriptions that are promotions do not have VAT applied! */ public function forGermany(Subscription $subscription): Subscription { if ($subscription->isPromotion) { return $subscription; } return $subscription->withPrice(new Price($subscription->price * 1.19)); } }
real context, not guesses 📐 Value Objects & Entities show how classes should look 🎯 Clear Bounded Context keeps suggestions on-topic ✅ Explicit invariants let AI write tests it understands
L P P H P S TA N – S TAT I C A N A LY S I S R E C TO R – A U TO M AT E D R E FACTO R I N G S Y M F O N Y I N S I G H T – P R O J E CT H E A LT H PHP-CS FIXER – CODE STYLE