Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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. Value Objects
    in PHP
    @ProchazkaFilip

    View Slide

  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

    View Slide

  3. Motivation

    View Slide

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

    View Slide

  5. Static types
    ● Prevent a whole class of bugs
    ○ Static analysis
    ● Make API’s clearer (documentation)
    ● Smarter autocompletion in IDE
    ● ...

    View Slide

  6. View Slide

  7. How not to do static typing
    https://twitter.com/jusrin00/status/875238742621028355

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  11. HTTP is a bunch of strings

    View Slide

  12. Good api: registration form
    class EmailAddress {
    private __construct(string $email) {}
    public static function fromString(string $email): EmailAddress
    {
    // validation
    return new EmailAddress($email);
    }

    View Slide

  13. Good api: registration form
    ● Private constructor + factory methods (yes, there can be more)
    ● Email can never be created invalid
    ● No duplication of logic

    View Slide

  14. Good api: registration form
    $email = EmailAddress::fromString(
    $form->getValues()->email
    );
    // ...
    $registration->register($email);

    View Slide

  15. Good api: registration form
    class RegistrationFacade {
    function register(EmailAddress $email): User {}
    class User {
    function __construct(EmailAddress $email) {}
    SAFE & STRICT

    View Slide

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

    View Slide

  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);

    View Slide

  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
    ● ….

    View Slide

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

    View Slide

  20. Good api: draw a pixel
    public function draw(
    Coordinates $coordinates,
    Color $color
    );
    $board->draw(
    new Coordinates(1, 2),
    Color::fromRGB(255, 105, 180)
    );

    View Slide

  21. Good api: draw a pixel
    ● Strictly typed
    ● Prefers domain concepts over primitives
    ● Self-documenting

    View Slide

  22. Value Objects: Behaviour (=> readability)
    $invertedColor = $color->invert();
    $transparentColor = $color->transparent(
    Percents::of(60) // 60%
    );

    View Slide

  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())

    View Slide

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

    View Slide

  25. Value Objects: Identity
    $colorA = Color::fromRGB(255, 105, 180);
    $colorB = Color::fromRGB(255, 105, 180);
    assert($colorA !== $colorB));
    assert($userJohnRef1 === $userJohnRef2));

    View Slide

  26. Value Objects: Immutable
    $invertedColor = $color->invert();
    assert($invertedColor !== $color);

    View Slide

  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)

    View Slide

  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

    View Slide

  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)

    View Slide

  30. @ProchazkaFilip

    View Slide

  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

    View Slide

  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
    ○ ...

    View Slide