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

Domain-driven Design Workshop

Domain-driven Design Workshop

FWDAYS May 21, 2020

Workshop: Building PHP applications using domain-driven design (DDD) techniques
The workshop is available only for participants of the PHP fwdays’20 conference who purchased a ticket till April 10. The form with registration for the workshop was sent to the email.

The workshop will be held in an online format in Zoom app. Please, install it on your device to follow the workshop. The number of participants is 30.

Workshop from Andrew Cassell is aimed at developers with Knowledge of PHP and object oriented programming. Audience and level: Intermediate and up. The workshop will be held in English.

Building PHP applications using domain-driven design (DDD) techniques results in code that is easier to modify, maintain, test, and makes for a better user experience. In this hands-on tutorial, you will become versed in the best practices for solving problems using domain-driven design in PHP from start to finish. You will learn to:

Discover a ubiquitous language and identify changes in the design of PHP classes, methods, and problem-solving
Use Event Storming build the best software for the client/user
Assemble an incorruptible domain model in PHP
Use bounded contexts and domain events to better organize code and make it more modular
Employ best practices for persisting and accessing entities and aggregate roots in Doctrine, Eloquent, Zend, etc
Use advanced PHP object-oriented techniques to simplify code and reduce state complexity
Plan for change by using the Hexagonal Architecture Pattern
Evaluate when and how to use CQRS and Event Sourcing techniques
Introduce DDD to a “legacy” codebase
Discover recommended resources for learning more about applying DDD in PHP

Andrew Cassell

May 21, 2020

More Decks by Andrew Cassell

Other Decks in Programming


  1. D ain-D iven Design Workshop D ain-D iven Design Workshop

    Andrew Cassell @alc277 andrewcassell.com
  2. 1 About CURIOSITYSTREAM • Founded by John Hendricks, founder of

    the Discovery Channel • Thousands of documentaries and series • HD and 4K • Available worldwide
  3. 26

  4. 27

  5. People don't want to buy a quarter-inch drill, they want

    a quarter- inch hole. Theodore Levitt
  6. DDD CRUD/MUD Recipe Building Inventory Brewhouse Activity Beer Menu /

    Prices Brew Sessions Brewery Website Calclations QA Record Keeping
  7. 1. Naming Things 2. Cache Invalidation 3. Off By One

    Errors Top 10 Reasons Programming is Hard
  8. Ubiquitous Language $recipe->addGrains($grains); Code: We need to be able to

    add a measured amount of grains to a recipe. Business:
  9. Ubiquitous Language $facilities = new StorageFacilities(); // from container $storage

    = $facilities->findByOneName(“Silo #4”); $storage->howMuchOfGrainType(new GrainType(‘Winter Wheat’)); Business: Code: We need to know how much Winter Wheat is in our storage silo #4.
  10. Ubiquitous Language $recipe->addDryHop($hopQuantity, $schedule); Code: We need to add .2oz

    of 12% AA per gallon of Simcoe hops to a recipe 7 days after the boil is complete. This is called dry hopping. $schedule = new DryHopSchedule(‘7 Days’); $hopQuantity = new HopQuantity(“Simcoe”,”12%AA”,”.2oz per gallon”); Business:
  11. $hop = (new HopsSupplier(…))->findOneByName(‘Simcoe’); $catalog = new RecipeCatalog(…); $recipe =

    $catalog->findOneRecipeByName(‘iPHPA’); $recipe->addDryHop($hop, new DryHopSchedule(‘7 Days’)); $brewers = new Brewers(…); $brewer = $brewers->findOneByUsername(‘cassell’); $brewSession = new BrewSession($brewer, $recipe, new Datetime()); $brewSession->dryHopsWereAdded($hop, new Datetime());
  12. $hop = (new HopsSupplierRepository(…))->findOneByName(‘Simcoe’); $catalog = new RecipeCatalogRepository(…); $recipe =

    $catalog->findOneRecipeByName(‘iPHPA’); $recipe->addDryHop($hop, new DryHopSchedule(‘7 Days’)); $brewers = new BrewersRepository(…); $brewer = $brewers->findOneByUsername(‘cassell’); $brewSession = new BrewSession($brewer, $recipe, new Datetime()); $brewSession->dryHopsWereAdded($hop, new Datetime());
  13. CRUD/MUD DDD Users Brewers String RecipeName Integer IBU Float(7,3) AlphaAcid

    Array of ORM Objects GrainBill BeerInventoryRepositoryInterface Menu
  14. […], we want to establish the idea that a computer

    language is not just a way of getting a computer to perform operations but rather that it is a novel formal medium for expressing ideas about methodology. Thus, programs must be written for people to read, and only incidentally for machines to execute.
  15. Photos: Terry Brown and Gordon Stettinius Alice Shane P.J. Sam

    George Male Age 32 Brewer for 3 Years Got into brewing after the record store he worked at closed. Is very tech savvy and carries an Android. Male Age 50 Brewing for 10 Years Just Promoted to COO Retired civil servant who believes software can make a brewery for regulatory compliant. Female Age 27 New Hire Library science graduate who was just hired away from the local university. iPhone user. Male Age 52 Librarian for 30 years Has been brewing beer as long as he can remember. Loves beer podcasts but is not a fan of technology. Male Age 46 Brewer for 20 Years Loves poetry and writing. Brewing is just a job to pay the bills.
  16. 7 Dirty Words When Meeting With a Domain Expert 1.Session

    2.Repository 3.Abstract 4.Interface 5.Class 6.Database 7.Foreign Key
  17. Domain Event • Beer Was Brewed • Grain Was Added

    • Hops Were Added • Brewer Changed Email Address
  18. Command • Start Brewing Session • Add Grain to Recipe

    • Add Hops To Fermenter • Change Email Address
  19. Actor • Who did it? • If you only have

    one user you might not need this
  20. Value Object No Identity (Only Values) *Immutable Hop Name Amount

    Paid Temperature Entity Identifiable Mutable Lifecycle Contains Value Objects Line Item Brewer Water Chemistry Aggregate Entity Responsible For Child Entities Transaction Boundary Invoice Recipe Brew Session
  21. Value Objects Email Address Recipe Name Date We Brewed On

    Weight Temperature Ubiquitous Language
  22. My pragmatic summary: A large fraction of the flaws in

    software development are due to programmers not fully understanding all the possible states their code may execute in.
  23. Plain Old PHP Objects (POPOs) • Declare Class Properties as

    Private • No Setters (behaviors will return new) • No References to Mutable Objects • Throw Exceptions in Constructor Value Objects
  24. cassell:beeriously cassell$ docker run --rm --interactive --tty --network beeriously_default --volume

    `pwd`:/app --user : --workdir /app beeriously_php-fpm /app/vendor/bin/phpunit --configuration /app/src/Tests/Unit/ phpunit.xml.dist PHPUnit 6.4.3 by Sebastian Bergmann and contributors. FEE 3 / 3 (100%) Time: 2.34 seconds, Memory: 4.00MB There were 2 errors: 1) Beeriously\Tests\Unit\Domain\Recipe\RecipeNameTest::testGetter Error: Class 'Beeriously\Domain\Recipe\RecipeName' not found /app/src/Tests/Unit/Domain/Recipe/RecipeNameTest.php:20 2) Beeriously\Tests\Unit\Domain\Recipe\RecipeNameTest::testToString Error: Class 'Beeriously\Domain\Recipe\RecipeName' not found /app/src/Tests/Unit/Domain/Recipe/RecipeNameTest.php:26 -- There was 1 failure: 1) Beeriously\Tests\Unit\Domain\Recipe\RecipeNameTest::testEmptyFails Failed asserting that exception of type "Error" matches expected exception "Beeriously\Domain\Recipe\InvalidRecipeNameException". Message was: "Class 'Beeriously\Domain\Recipe\RecipeName' not found" at /app/src/Tests/Unit/Domain/Recipe/RecipeNameTest.php:15 . ERRORS! Tests: 3, Assertions: 1, Errors: 2, Failures: 1.
  25. cassell:beeriously cassell$ docker run --rm --interactive --tty --network beeriously_default --volume

    `pwd`:/app --user : --workdir /app beeriously_php-fpm /app/vendor/bin/phpunit --configuration /app/src/Tests/ Unit/phpunit.xml.dist PHPUnit 6.4.3 by Sebastian Bergmann and contributors. ... 3 / 3 (100%) Time: 192 ms, Memory: 4.00MB OK (3 tests, 4 assertions)
  26. <?php var_dump(-0.0 > 0); // false var_dump(-0.0 < 0); //

    false var_dump(-0.0 === 0.0); // true (float) var_dump(-0.0 === 0); // false (int) var_dump(-0.0 == false); // true (bool) var_dump(-0.0 == null); // true (bool) var_dump(-0.0 == “”); // true var_dump(-0.0 == "negativezero"); // true (string)
  27. Sugar + Yeast = Alcohol + CO 2 C H

    O ->2C H OH + 2CO 6 12 6 2 5 2
  28. Entities Identifiable Have State and are Mutable (Lifecycle) Operate using

    Value Objects *(Ideally) Storage Agnostic *(Ideally) Never in an Invalid State
  29. Making an Entity: Brewer Identifiable ID (UUID) Mutable $brewer->changeUsername() $brewer->changeName()

    $brewer->changeEmail() Never in an Invalid State __construct Operate Using Value Objects FullName, EmailAddress, …
  30. $book->setPrice(16.96); $amount = new Amount(16.96, new Currency(‘USD’); $price = new

    BookPrice($book, $amount); $price->change(new Amount(15.99, new Currency(‘USD’)); Mutable Immutable
  31. <script src="immutable.min.js"></ script> <script> var map1 = Immutable.Map({ a: 1,

    b: 2, c: 3 }); var map2 = map1.set('b', 50); map1.get('b'); // 2 map2.get('b'); // 50 </script>
  32. Mathias Verraes - Decoupling the Model from the Framework at

    Laracon EU 2014 https://www.youtube.com/watch?v=QaIGN_cTcc8
  33. Ruby Midwest 2011 - Keynote: Architecture the Lost Years by

    Robert Martin https://www.youtube.com/watch?v=WpkDN78P884
  34. Domain Events • Part of the Core Domain • Happened

    In The Past • Important Enough To Record (Persist) • Important Enough To Concern Other Bounded Contexts • Immutable Value Objects • Do Not Contain Entities or Other Mutable Objects • Can Be Created in an Entity
  35. DDD BALL OF MUD/CRUD Correctness ✓✓✓ Testability ✓✓ Usability ✓✓

    Maintainability ✓✓✓✓ Modifiability ✓✓✓
  36. Event Sourcing • Object Properties are Not Persisted • Events

    Are Persisted to Append Only Event Storage
  37. • Avoids Object-relational Impedance Mismatch • Reduces Database Table Counts

    (Related Tables) • Potentially Reduces Model Counts Event Sourcing
  38. Recipe was created with name “New Recipe” Recipe name was

    changed to “php[world] IPhpA” 11 pounds of 2-Row Malted Barley was added 2 pounds of Winter Wheat was added 1 pound of Vienna Malt was added 8 gallons of water was added 2 ounces of Centennial Hops for 60 Minutes 1 ounce of Centennial Hop added at whirlpool
  39. DDD Topics Covered •Ubiquitous Language •Event Storming •Modelling •Value Objects

    •Entities •Aggregates •Hexagonal Architecture •CQRS •Event Sourcing @alc277 andrewcassell.com