Value objects in PHP

Value objects in PHP

Crafting better API's using types & Value Objects

07ac3a80e69a6252140feb81b89cbb08?s=128

Filip Procházka

November 07, 2018
Tweet

Transcript

  1. Value Objects in PHP @ProchazkaFilip

  2. • +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
  3. Motivation

  4. PHP 7.0 is dead, long live the PHP 7.2

  5. Static types • Prevent a whole class of bugs ◦

    Static analysis • Make API’s clearer (documentation) • Smarter autocompletion in IDE • ...
  6. None
  7. How not to do static typing https://twitter.com/jusrin00/status/875238742621028355

  8. Bad api: registration form $email = $form->getValues()->email; // ... $registration->register($email);

    // ... $user = new User($email); // ...
  9. Bad api: registration form class RegistrationFacade { function register(string $email):

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

    User { // validation } class User { function __construct(string $email) { // validation } DUPLICATION
  11. HTTP is a bunch of strings

  12. Good api: registration form class EmailAddress { private __construct(string $email)

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

    (yes, there can be more) • Email can never be created invalid • No duplication of logic
  14. Good api: registration form $email = EmailAddress::fromString( $form->getValues()->email ); //

    ... $registration->register($email);
  15. Good api: registration form class RegistrationFacade { function register(EmailAddress $email):

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

    int $y, int $red, int $green, int $blue );
  17. 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);
  18. 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 • ….
  19. Good api: draw a pixel public function draw( Coordinates $coordinates,

    Color $color );
  20. Good api: draw a pixel public function draw( Coordinates $coordinates,

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

    domain concepts over primitives • Self-documenting
  22. Value Objects: Behaviour (=> readability) $invertedColor = $color->invert(); $transparentColor =

    $color->transparent( Percents::of(60) // 60% );
  23. 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())
  24. Value Objects: Equality $colorA = Color::fromRGB(255, 105, 180); $colorB =

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

    Color::fromRGB(255, 105, 180); assert($colorA !== $colorB)); assert($userJohnRef1 === $userJohnRef2));
  26. Value Objects: Immutable $invertedColor = $color->invert(); assert($invertedColor !== $color);

  27. 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)
  28. 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
  29. 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)
  30. @ProchazkaFilip

  31. 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
  32. 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 ◦ ...