Lock in $30 Savings on PRO—Offer Ends Soon! ⏳

Value objects in PHP

Value objects in PHP

Crafting better API's using types & Value Objects

Filip Procházka

November 07, 2018
Tweet

More Decks by Filip Procházka

Other Decks in Technology

Transcript

  1. • +12 years of programming ◦ Mostly backend PHP ◦

    CTO & Architect on big local projects ◦ Lately backend Java • Trainings for ◦ Git ◦ Testing ◦ Correct™ usage of ORM @ProchazkaFilip
  2. Static types • Prevent a whole class of bugs ◦

    Static analysis • Make API’s clearer (documentation) • Smarter autocompletion in IDE • ...
  3. Bad api: registration form class RegistrationFacade { function register(string $email):

    User {} class User { function __construct(string $email) {} HOPE
  4. Bad api: registration form class RegistrationFacade { function register(string $email):

    User { // validation } class User { function __construct(string $email) { // validation } DUPLICATION
  5. Good api: registration form class EmailAddress { private __construct(string $email)

    {} public static function fromString(string $email): EmailAddress { // validation return new EmailAddress($email); }
  6. Good api: registration form • Private constructor + factory methods

    (yes, there can be more) • Email can never be created invalid • No duplication of logic
  7. Good api: registration form class RegistrationFacade { function register(EmailAddress $email):

    User {} class User { function __construct(EmailAddress $email) {} SAFE & STRICT
  8. Bad api: draw a pixel public function draw( int $x,

    int $y, int $red, int $green, int $blue );
  9. Bad api: draw a pixel public function draw( int $x,

    int $y, int $red, int $green, int $blue ); $board->draw(1, 2, 255, 105, 180);
  10. Bad api: draw a pixel • Doesn’t prevent “stupid mistakes”

    • “Works” even when you pass the wrong value ◦ Misplaced coordinates & color RGB values • Too many arguments • Bad variable names • Duplicated validation • ….
  11. Good api: draw a pixel public function draw( Coordinates $coordinates,

    Color $color ); $board->draw( new Coordinates(1, 2), Color::fromRGB(255, 105, 180) );
  12. Good api: draw a pixel • Strictly typed • Prefers

    domain concepts over primitives • Self-documenting
  13. Value Objects: Equality $colorA = Color::fromRGB(255, 105, 180); $colorB =

    Color::fromRGB(255, 105, 180); assert($colorA->getRed() === $colorB->getRed() && $colorA->getGreen() === $colorB->getGreen() && $colorA->getBlue() === $colorB->getBlue())
  14. Value Objects: Equality $colorA = Color::fromRGB(255, 105, 180); $colorB =

    Color::fromRGB(255, 105, 180); assert($colorA->equals($colorB));
  15. Value Objects: Identity $colorA = Color::fromRGB(255, 105, 180); $colorB =

    Color::fromRGB(255, 105, 180); assert($colorA !== $colorB)); assert($userJohnRef1 === $userJohnRef2));
  16. Value Objects: Immutable $a = new \DateTime('2018-01-01 00:00:00.000'); $b =

    $a->modify('+1 minute'); assert($a === $b) $a = new \DateTimeImmutable('2018-01-01 00:00:00.000'); $b = $a->modify('+1 minute'); assert($a !== $b)
  17. Value objects: few ideas • No Time & Date, only

    Datetime ◦ https://github.com/brick/date-time • Money and Currency API ◦ https://github.com/brick/money • EmailAddress • PhoneNumber • Url • DateRange, TimeRange, NumberRange • PostalAddress
  18. Summary: Value Objects • Define a concept of your domain

    • Defined by values, not identity • Valid from the moment of creation • Immutable (preferably) • Should have behaviour • VO is not ◦ Data Transfer Object ◦ Reference Object (Entity)
  19. Bonus: VO vs RO (Entity) • Reference Object ◦ Is

    defined by identity (we don’t care about equality) ◦ Has behaviour • Examples ◦ User, Order, … ◦ Address VO vs Address Entity
  20. Bonus: VO vs DTO • Data Transfer Object ◦ Is

    a dummy structure ◦ No behaviour ◦ We don’t care about equality/identity ◦ Groups together related data • Example: ◦ API Request object structure (for mapping from JSON) ◦ Form data ◦ ...