$30 off During Our Annual Pro Sale. View Details »

Domain modeling with PHP / domain-modeling-with-php-en

shin1x1
October 17, 2021

Domain modeling with PHP / domain-modeling-with-php-en

shin1x1

October 17, 2021
Tweet

More Decks by shin1x1

Other Decks in Programming

Transcript

  1. Domain modeling with PHP
    2021/10/17 @shin1x1

    View Slide

  2. @shin1x1
    Masashi Shinbara
    Freelance Web Developer
    Podcast(Japanese)
    https://php-genba.shin1x1.com/

    View Slide

  3. Sample code
    https://github.com/shin1x1/domain-modeling-with-php

    View Slide

  4. Miscommunication in software development
    4

    View Slide

  5. ex. Same word, different concept
    The same concept can be expressed with different words.
    5

    View Slide

  6. ex. Some features dropped out in conversation
    6

    View Slide

  7. ex. Cannot recall the original concept from code
    7

    View Slide

  8. Let's create shared concept!
    8

    View Slide

  9. Let's create shared concept!
    9

    View Slide

  10. Domain model
    Domain: The target domain that the software deals with. In the case of a business
    application, the target business is the domain.
    Model: A concept that extracts the elements necessary for a purpose from a
    target.
    10

    View Slide

  11. Domain model
    A concept that extracts the necessary elements from a domain.
    Sometimes the necessary concepts are created or modified.
    Usually does not include the technical elements of the system. (Except when the
    technical element is a domain.)
    11

    View Slide

  12. Today's domain
    12

    View Slide

  13. The domain in this announcement is fictitious and
    has nothing to do with the real one.
    13

    View Slide

  14. Vaccination System
    Vaccinations will be administered at the vaccination site after prior appointment.
    Vaccination is currently given only once.
    Establish a system for making vaccination reservations and registering vaccinees.
    14

    View Slide

  15. Vaccination flow
    15

    View Slide

  16. Modeling
    16

    View Slide

  17. Modeling
    Extract the elements of the domain required for systemization.
    Gradually shape them using multiple perspectives and methods.
    Macro perspective
    Conceptual model diagram, use case diagram
    Micro perspective
    Glossary, use case scenarios, code
    Obtain feedback from the business team to improve the accuracy.
    17

    View Slide

  18. Use case diagrams
    Indicates the relationship between users and system-provided functions.
    Indicate the scope of systemization (what to include in the system and what not to
    include).
    Local governments and doctors are not users of this system.
    18

    View Slide

  19. Create glossary
    Compile a table of domain terms from business and requirement documents,
    interviews, etc.
    Terms, meanings, constraints, etc.
    Give the same name to the same concept.
    Important for communication.
    If there is another name, include it.
    Include not only nouns, but also actions and events.
    19

    View Slide

  20. Example of glossary
    terms content
    recipient A person who receives a vaccination.
    reserve The act of reserving a vaccination by a recipient.
    Only unreserved recipients can be reserved.
    reserved
    date
    The date of vaccination.
    The date of the vaccination is specified by the recipient in the
    reservation.
    After 7 days but within 30 days from the date of reservation
    registration.
    20

    View Slide

  21. Create a conceptual model (domain model)
    diagram
    Arrange the terms in the glossary as a simple class diagram.
    Organize the relationships between each term by connecting them with lines or
    grouping them.
    Look at the terms from a bird's eye view to see if there are any discrepancies or
    omissions.
    21

    View Slide

  22. Example of conceptual model diagram
    22

    View Slide

  23. Implement and verify in code
    23

    View Slide

  24. Implement and verify in code
    Implemented in code as part of modeling.
    Implement in code as part of modeling.
    24

    View Slide

  25. Implement domain model
    1 concept = 1 class.
    By making it a class, you can benefit from type checking.
    Implemented in POPO (Plain Old PHP Object).
    Use domain model terminology for class and method names.
    Confine the constraints of the model to the implementation of the class.
    Make it an immutable object.
    Do not create setter methods.
    Change property values according to domain logic.
    (ex. use reserve() instead of setReservation())
    25

    View Slide

  26. Vaccination ticket code class
    Implement the constraint of the vaccination ticket no(10 digits) in the constructor.
    Instantiation = constraint is satisfied.
    final class VaccinationTicketNo

    {

    public function __construct(private string $code)

    {

    if (preg_match('/\A[0-9]{10}\z/', $code) ! == 1) {

    throw new InvariantException('Invalid code:' . $code);

    }
    }

    }

    26

    View Slide

  27. Test the vaccination ticket code class
    POPO, so easy to test.
    Make sure that exceptions are thrown if constraints are violated.
    /**

    * @test

    */

    public function construct_invalid_code(): void

    {

    $this->expectException(InvariantException::class);

    new VaccinationTicketNo('A234567890');

    }

    27

    View Slide

  28. Reserved date class
    Takes a string indicating the reserved date and the current date in the Factory
    method, and validates the domain rule. (After 7 days, but within 30 days)
    final class ReservedDate

    {

    public function __construct(private Date $date)

    {

    }

    public static function createFromString(string $dateString, Date $now): self

    {

    $date = Date::createFromString($dateString);



    // TODO: throw an exception if $now is not within 30 days after 7 days

    return new self($date);

    }

    }

    28

    View Slide

  29. RecipientId class
    Indicates the identifier of the recipient.
    Identifiers are often the arguments of methods, and benefit greatly from type
    checking.
    Identifiers are often used as arguments to methods, and benefit from type
    checking.
    Since other IDs in a project are often implemented in the same way, it is easier to
    implement them if they are standardized using traits.
    final class RecipientId

    {

    use SequencialId;

    }

    29

    View Slide

  30. Recipient class
    Has properties for recipient ID, appointment, and inoculation.
    The constraints of each value are implemented in each class.
    Reservation and vaccination are set to nullable because they may not have values.
    final class Recipient

    {

    public function __construct(

    private RecipientId $id,

    private ?Reservation $reservation = null,

    private ?Vaccination $vaccination = null,

    ) {

    }

    30

    View Slide

  31. Recipient class - reserve method
    Implement the domain logic of registering an appointment in the method.
    If there is already a reservation, it is assumed that the reservation is complete and
    an exception is thrown.
    Create and return a new instance containing the reservation.
    public function reserve(Reservation $reservation): self

    {

    if ($this->reservation ! == null) {

    throw new PreconditionException();

    }

    return new self(

    $this->id,

    $reservation,

    );

    }
    31

    View Slide

  32. Test the reserve method
    Check that the new instance created by the register-reservation method contains
    a reservation.
    /**

    * @test

    */

    public function reserve()

    {

    $sut = new Recipient(new RecipientId());

    $reservation = new Reservation(

    new ReservedDate(Date::createFromString('2021-09-19')));



    $actual = $sut->reserve($reservation);

    $expected = new Recipient(new RecipientId(), reservation: $reservation, vaccination: null);

    $this->assertEquals($expected, $actual);

    }

    32

    View Slide

  33. Implementation as part of modeling
    Modeling and implementing the code gives you a better understanding.
    It eliminates ambiguity so you can find inconsistencies and flaws in the
    model.
    In fact, you often notice them by implementing them in code (right?). It can
    be implemented in code.
    Can be executed and verified in tests.
    The advantage of modeling by someone who can write code.
    33

    View Slide

  34. Verify with use cases
    34

    View Slide

  35. Validate the model in use cases
    Validation using domain models in use case scenarios.
    Use case description
    Describe the use case scenario in text.
    35

    View Slide

  36. Example of use case description - reserve
    Main actors
    Recipient
    Preconditions (conditions to be fulfilled before the scenario execution)
    The recipient has a vaccination ticket.
    The recipient has not completed the reservation or the vaccination.
    The recipient has neither an reservation nor a completed vaccination.
    Post-conditions (conditions to be fulfilled after the scenario execution)
    The recipient's reservation is registered.
    36

    View Slide

  37. Basic flow
    The recipient accesses the reservation screen.
    The recipient enters and sends the inoculation ticket number, the
    municipality number, and the reserved inoculation date.
    The system identifies the recipient from the vaccination ticket code and the
    municipality number.
    The system registers the reservation.
    37

    View Slide

  38. Alternative flow (other than basic flow, abnormal flow)
    In the following cases, an input error shall occur.
    There is no recipient corresponding to the vaccination ticket code or
    municipality number.
    The corresponding recipient has already made an appointment or
    completed the inoculation.
    The reserved date is beyond the range of 7 days to 30 days from the
    current date.
    38

    View Slide

  39. Use case scenario implementation
    1 use case scenario = 1 class.
    Only one public method is used for scenario execution.
    Domain-related processing is implemented by domain objects.
    Implement with POPO to limit the responsibilities and make testing easier.
    Processing related to IO such as database are abstracted by interfaces.
    39

    View Slide

  40. Example of IO abstraction with interfaces
    https://speakerdeck.com/shin1x1/independent-core-layer-pattern-phpconsen2019
    40

    View Slide

  41. Implement the reserved registration use case class
    Create and retrieve domain objects and execute the domain logic.
    Save the object updated by the domain logic to the database.
    final class ReservationUseCase

    {

    public function run(VaccinationTicketNo $vaccinationTicketNo,

    MunicipalityNo $municipalityNo, ReservedDate $reservedDate): void

    {

    // Get the domain object from the database

    $recipient = $this->query->find($vaccinationTicketNo, $municipalityNo);

    if ($recipient === null) {

    throw new PreconditionException('The recipient does not exist');

    }
    // Execute the domain logic

    $recipient = $recipient->reserve(new Reservation($reservedDate));

    // Store the resulting domain object in the database

    $this->command->store($recipient);

    }

    }

    41

    View Slide

  42. Model, code improvements
    42

    View Slide

  43. Vaccination status determination
    Determination of recipient status, such as reserved or inoculated, is based on the
    value of the reservation or vaccination property.
    It is possible to have an invalid state where the reservation has no value and
    the vaccination has a value.
    As the number of variations in the recipient status increases, the pattern of
    transitions also increases.
    Add an recipient status that clearly indicates it.
    43

    View Slide

  44. Conceptual model diagram
    Add inoculation status to the domain model.
    44

    View Slide

  45. State transitions for recipient status
    State transition rules are shown in activity diagrams to clarify transition patterns.
    Transitions that are not indicated in the diagram will result in an error.
    45

    View Slide

  46. Implementation of inoculation status.
    Representation of states with enums (introduced in PHP 8.1).
    Enums can be treated as types.
    enum VaccinationStatus

    {

    case Unreserved;

    case Reserved;
    case Vaccinated;

    }

    46

    View Slide

  47. Change the Recipient class.
    Add recipient status in constructor. Initial value should be unreserved.
    final class Recipient

    {

    public function __constructor(

    private VaccinatorId $id,

    private VaccinationStatus $recipientStatus = VaccinationStatus::Unreserved,

    private ?Reservation $reservation = null,

    private ?Vaccination $vaccination = null,

    ) {}

    47

    View Slide

  48. Change Recipient class - reserve method
    Do pre-condition validation for reserve by looking at the recipient status.
    Changed cancel reservation and vaccinate methods as well.
    public function reserve(Reservation $reservation): self

    {

    if ($this->reservationStatus ! == ReservationSttus::Unreserved) {

    throw new PreconditionException('Cannot reserve with current status');

    }
    return new self(

    $this->id,

    ReservationStatus::Reserved,

    $reservation,

    );

    }

    48

    View Slide

  49. No change to the reserved use case
    No change in use case classes, since only domain classes are changed.
    final class ReservationUseCase

    {

    public function run(VaccinationTicketNo $vaccinationTicketNo,

    MunicipalityNo $municipalityNo, ReservedDate $reservedDate): void

    {

    // Get the domain object from the database

    $recipient = $this->query->find($vaccinationTicketNo, $municipalityNo);

    if ($recipient === null) {

    throw new PreconditionException('The recipient does not exist');

    }
    // Execute the domain logic

    $recipient = $recipient->reserve(new Reservation($reservedDate));

    // Store the resulting domain object in the database

    $this->command->store($recipient);

    }

    }
    49

    View Slide

  50. Summary
    50

    View Slide

  51. Summary
    Construct a domain model by extracting the necessary elements from the domain.
    Share the domain model as a common concept.
    Implement the vocabulary and knowledge of the domain model into domain
    classes.
    Try modeling because you can write code!!!
    51

    View Slide

  52. I'm looking forward to your feedback!
    https://joind.in/talk/650b0
    52

    View Slide

  53. Where do I start?
    Start with a glossary.
    Just creating a glossary and aligning your perceptions will help you understand a
    lot.
    People who will join the project later will also be happy.
    And maybe a use case diagram.
    53

    View Slide

  54. How to maintain diagrams and documents?
    Of course, it is ideal to do so.
    There is also the idea of as a communication tool, leaving it as a snapshot on a
    wiki, etc.
    If you use PlantUML, you can give up the layout adjustment :)
    54

    View Slide

  55. How to maintain diagrams and documents?
    The implementation of the domain model is also a component in the overall
    system, so implement it as needed.
    There are constraints and logic of the domain.
    There are domain constraints and logic.
    You want to indicate with something without logic, such as an identifier.
    If you want to do type checking.
    If not, a scalar type is fine.
    55

    View Slide

  56. References
    Domain-Driven Design: Tackling Complexity in the Heart of Software
    https://www.amazon.com/gp/product/0321125215
    Domain Modeling Made Functional: Tackle Software Complexity with Domain-
    Driven Design and F#
    https://pragprog.com/titles/swdddf/domain-modeling-made-functional/
    Object oriented Model (Japanese)
    http://www.ics.kagoshima-u.ac.jp/edu/SoftwareEngineering/oo-model.html
    The story of visualizing an existing system through modeling (Japanese)
    https://speakerdeck.com/jnuank/moderingudeji-cun-sisutemufalseke-shi-hua-
    nilin-ndahua
    56

    View Slide