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

php[world] 2017 Domain-Driven Design Workshop

Andrew Cassell
November 15, 2017
230

php[world] 2017 Domain-Driven Design Workshop

Andrew Cassell

November 15, 2017
Tweet

Transcript

  1. Domain-Driven
    Design
    Deconstructed
    Domain-Driven
    Design
    Deconstructed
    Andrew Cassell
    @alc277 andrewcassell.com

    View Slide

  2. View Slide

  3. View Slide

  4. View Slide

  5. View Slide

  6. View Slide

  7. View Slide

  8. Domain-Driven
    Design

    View Slide

  9. Domain-Driven
    Design

    View Slide

  10. View Slide

  11. Common Sense
    Software
    Development

    View Slide

  12. View Slide

  13. View Slide

  14. View Slide

  15. View Slide

  16. View Slide

  17. View Slide

  18. View Slide

  19. View Slide

  20. 20

    View Slide

  21. View Slide

  22. Encapsulation &
    Immutability &
    Modeling &
    Behavior

    View Slide

  23. Domain

    View Slide

  24. Domain

    View Slide

  25. BEER

    View Slide

  26. View Slide

  27. View Slide

  28. View Slide

  29. View Slide

  30. Photo: Kathleen Pierce - Bangor Daily News

    View Slide

  31. View Slide

  32. View Slide

  33. View Slide

  34. View Slide

  35. View Slide

  36. View Slide

  37. View Slide

  38. View Slide

  39. View Slide

  40. View Slide

  41. View Slide

  42. Brewing
    Software

    View Slide

  43. View Slide

  44. View Slide

  45. View Slide

  46. View Slide

  47. View Slide

  48. View Slide

  49. View Slide

  50. View Slide

  51. View Slide

  52. View Slide

  53. View Slide

  54. recipe
    hop
    grain
    hop_inventory
    grain_inventory
    recipe_hop_link
    user
    recipe_user_link
    brew_event
    ROLE_USER
    ROLE_ADMIN

    View Slide

  55. Source: http://jonclaytonbiz.com/

    View Slide

  56. View Slide

  57. View Slide

  58. People don't want to buy
    a quarter-inch drill, they
    want a quarter-inch hole.
    Theodore Levitt

    View Slide

  59. View Slide

  60. View Slide

  61. View Slide

  62. #JBTD
    (http://jobstobedone.org/)

    View Slide

  63. View Slide

  64. YYYYY

    View Slide

  65. Why?
    Why?
    Why?
    Why?
    Why?

    View Slide

  66. Search
    Caluclation
    List
    Automation

    View Slide

  67. DDD !=

    View Slide

  68. View Slide

  69. View Slide

  70. View Slide

  71. View Slide

  72. View Slide

  73. http://www.csharpstar.com/

    View Slide

  74. View Slide

  75. DDD CRUD
    Correctness
    Testability
    Usability
    Maintainability
    Modifiability

    View Slide

  76. View Slide

  77. DDD BALL OF MUD / CRUD
    Correctness
    Testability
    Usability
    Maintainability
    Modifiability

    View Slide

  78. DDD CRUD/MUD
    Recipe Building Inventory
    Brewhouse Activity Beer Menu / Prices
    Brew Sessions Brewery Website
    Calclations QA Record Keeping

    View Slide

  79. Who is
    responsible

    View Slide

  80. View Slide

  81. View Slide

  82. View Slide

  83. View Slide

  84. Ubiquitous
    Language

    View Slide

  85. Ubiquitous Language
    Developers
    Domain
    Experts

    View Slide

  86. Ubiquitous
    Language

    View Slide

  87. User
    Brewer
    Employee
    Taster
    Customer

    View Slide

  88. View Slide

  89. 1. Naming Things
    2. Cache Invalidation
    3. Off By One Errors
    Top 10 Reasons
    Programming is Hard

    View Slide

  90. $recipe = BeerRecipe::findOrFail($uuid);

    $hop = HopRepo::where(“name”,”=“,”Simcoe”)->take(1)->get();

    if (!$recipe->hopsArray()->contains(‘hop_id’,$hop->id)) {


    $recipe->hopsArray()->attach($hop->id, [‘weight' => 1.0]);

    }



    $recipe->save();

    View Slide

  91. View Slide

  92. Ubiquitous Language
    $recipe->addGrains($grains);
    Code:
    We need to be able to add a measured amount
    of grain to a recipe.
    Business:

    View Slide

  93. Ubiquitous Language
    $recipe->getEstimatedAlcoholByVolumeABV();
    Code:
    We need to be able to calculate an estimated
    ABV for a recipe.
    Business:

    View Slide

  94. Ubiquitous Language
    $storage = (new StorageFacilities())->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.

    View Slide

  95. 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 = HopQuantity::(“Simcoe”,”12%AA”,”.2oz per gallon”);
    Business:

    View Slide

  96. @recipe
    Feature: A user should be able to dry hop a recipe
    Scenario: User adds dry hops to recipe
    Given I am authenticated as a user
    And I am on “/recipe/0ef360fd“
    And I press "Add Hops”
    And I fill out form with:
    | Hop Name |
    | Simcoe |
    Then I see “Alpha Acid: 11.5 - 15.0“

    View Slide

  97. $recipe->dryHop($hop);
    $recipe->addIngredient($hop);
    $recipe->addToBoil($hop);
    $recipe->addToBoilKettle($hop);
    $recipe->addToBoiler($hop);
    $recipe->addFirstWortHop($hop);
    $recipe->addToFermenter($hop);
    $recipe->addToMashTun($hop); //fails

    View Slide

  98. $hop = (new HopsSupply(…))->findOneByName(‘Simcoe’);
    $catalog = new RecipeCatalog(…);
    $recipe = $catalog->findOneRecipeByName(‘iPHPA’);
    $recipe->addDryHop($hop, new DryHopSchedule(‘7 Days’));
    $brewers = new Brewers(…);
    $brewer = $brewers->findOneByUsername(‘cassell’);
    $brewSession = $brewer->brew($recipe);
    $brewSession->dryHopsWereAdded($hop, Datetime::now());

    View Slide

  99. $hop = (new HopsSupplyRepo())->findOneByName(‘Simcoe’);
    $catalog = new RecipeCatalogRepository(…);
    $recipe = $catalog->findOneRecipeByName(‘iPHPA’);
    $recipe->addDryHop($hop, new DryHopSchedule(‘7 Days’));
    $brewers = new BrewersRepository(…);
    $brewer = $brewers->findOneByUsername(‘cassell’);
    $brewSession = $brewer->brew($recipe);
    $brewSession->dryHopsWereAdded($hop, Datetime::now());

    View Slide

  100. CRUD/MUD DDD
    Users Brewers
    String RecipeName
    Integer IBU
    Float(7,3) AlphaAcid
    Array of ORM Objects GrainBill
    BeerInventoryRepositoryInterface Menu

    View Slide

  101. View Slide

  102. View Slide

  103. View Slide

  104. $hop = (new HopsSupply())->findOneByName(‘Centennial Pellets’);
    $catalog = new RecipeCatalog(…);
    $recipe = $catalog->findOneRecipeByName(‘iPHPA’);
    $recipe->addDryHop($hop, new DryHopSchedule(‘7 Days’));
    $brewers = new Brewers(…);
    $brewer = $brewers->findOneByUsername(‘cassell’);
    $brewSession = $brewer->brew($recipe);
    $brewSession->dryHopsWereAdded($hop, Datetime::now());

    View Slide

  105. […], 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.

    View Slide

  106. DDD BALL OF MUD/CRUD
    Correctness ✓
    Testability
    Usability
    Maintainability
    Modifiability

    View Slide

  107. View Slide

  108. The Brewer, designed and engraved in the Sixteenth Century, by Jost Amman.

    View Slide

  109. Inventory
    Marketing
    Finance
    Production
    “Hop”

    View Slide

  110. Quantity at a Location
    Flavor
    Expenditure
    Ingredient
    “Hop”

    View Slide

  111. Bounded
    Contexts

    View Slide

  112. Inventory
    Recipes Brewhouse

    View Slide

  113. Slide: Vaughn Vernon

    View Slide

  114. View Slide

  115. Inventory
    Recipes Brewhouse

    View Slide

  116. Inventory
    Recipes Brewhouse

    View Slide

  117. Message
    Inventory
    Recipes Brewhouse

    View Slide

  118. Domain Event
    Inventory
    Recipes Brewhouse

    View Slide

  119. View Slide

  120. View Slide

  121. View Slide

  122. View Slide

  123. View Slide

  124. Personas

    View Slide

  125. adaptivepath.com

    View Slide

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

    View Slide

  127. View Slide

  128. View Slide

  129. View Slide

  130. View Slide

  131. https://blog.intercom.io/using-job-stories-design-features-ui-ux/

    View Slide

  132. View Slide

  133. View Slide

  134. 7 Dirty Words When Meeting With a Domain Expert
    1.Session
    2.Repository
    3.Abstract
    4.Interface
    5.Class
    6.Database
    7.Foreign Key

    View Slide

  135. User Research

    View Slide

  136. Photo: Mathias Verraes

    View Slide

  137. Command
    Actor
    Model
    Domain
    Event

    View Slide

  138. Domain Event
    • Something that happens in the
    software.
    • Past Tense

    View Slide

  139. Domain Event
    • Beer Was Brewed
    • Grain Was Added
    • Hops Were Added
    • Brewer Changed Email Address

    View Slide

  140. View Slide

  141. View Slide

  142. View Slide

  143. View Slide

  144. View Slide

  145. View Slide

  146. View Slide

  147. View Slide

  148. View Slide

  149. Command
    Actor
    Model
    Domain
    Event

    View Slide

  150. Command
    • The action that precipitates the event
    • Written in Present Tense

    View Slide

  151. Command
    • Start Brewing Session
    • Add Grain to Recipe
    • Add Hops To Fermenter
    • Change Email Address

    View Slide

  152. View Slide

  153. View Slide

  154. Command
    Actor
    Model
    Domain
    Event

    View Slide

  155. Model
    • Command is acted upon this thing
    • Might Include Supporting Models

    View Slide

  156. Model
    • Recipe
    • Ingredient
    • Brewer

    View Slide

  157. View Slide

  158. Command
    Actor
    Model
    Domain
    Event

    View Slide

  159. Actor
    • Who did it?
    • If you only have one user you might
    not need this

    View Slide

  160. View Slide

  161. View Slide

  162. Command
    Actor
    Model
    Domain
    Event

    View Slide

  163. View Slide

  164. View Slide

  165. business
    origami

    View Slide

  166. Jess McMullin
    Business Origami

    View Slide

  167. Jess McMullin
    Business Origami

    View Slide

  168. Jess McMullin
    Business Origami

    View Slide

  169. Sean Jalleh

    View Slide

  170. View Slide

  171. View Slide

  172. Source: Jeff Patton

    View Slide

  173. View Slide

  174. View Slide

  175. View Slide

  176. View Slide

  177. View Slide

  178. DDD BALL OF MUD/CRUD
    Correctness ✓✓
    Testability
    Usability ✓
    Maintainability
    Modifiability

    View Slide

  179. Domain Objects
    Value Objects
    Domain Events
    Entities
    Aggregates

    View Slide

  180. Value Object
    Immutable
    No Identity (Only Values)
    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

    View Slide

  181. Value Objects

    View Slide

  182. Email Address
    Recipe Name
    Date We Brewed On
    Weight
    Temperature
    Value Objects

    View Slide

  183. Value Objects
    Email Address
    Recipe Name
    Date We Brewed On
    Weight
    Temperature
    Ubiquitous
    Language

    View Slide

  184. Value Objects
    EmailAddress
    RecipeName
    BrewedOn
    Pounds
    DegreesFahrenheit
    Email Address
    Recipe Name
    Date We Brewed On
    Weight
    Temperature

    View Slide

  185. Not Just Static Typing
    https://github.com/Fiedzia/type-system-research/blob/master/README.md
    Value Objects

    View Slide

  186. Immutable

    View Slide

  187. Immutable
    Avoid Spooky
    Action at a
    Distance

    View Slide

  188. ALWAYS
    VALID
    Value Objects

    View Slide

  189. Value Objects

    View Slide

  190. “Gateway Drug to
    Test Driven Development”
    Value Objects

    View Slide

  191. “Gateway Drug to
    Test Driven Development”
    Value Objects
    #MYTESTSDONTPASS

    View Slide

  192. Plain PHP Objects
    (POPOs)
    Value Objects

    View Slide

  193. Plain 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

    View Slide

  194. Only depend on
    scalars or other value
    objects.
    Value Objects

    View Slide

  195. They are not
    dependencies that
    you inject.
    Value Objects

    View Slide

  196. RecipeName
    Example

    View Slide

  197. View Slide

  198. View Slide

  199. View Slide

  200. View Slide

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

    View Slide

  202. View Slide

  203. 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.
    .EE 3 / 3 (100%)
    Time: 1.56 seconds, Memory: 4.00MB
    There were 2 errors:
    1) Beeriously\Tests\Unit\Domain\Recipe\RecipeNameTest::testGetter
    Error: Call to undefined method Beeriously\Domain\Recipe\RecipeName::getValue()
    /app/src/Tests/Unit/Domain/Recipe/RecipeNameTest.php:21
    2) Beeriously\Tests\Unit\Domain\Recipe\RecipeNameTest::testToString
    Object of class Beeriously\Domain\Recipe\RecipeName could not be converted to string
    /app/src/Tests/Unit/Domain/Recipe/RecipeNameTest.php:27
    ERRORS!
    Tests: 3, Assertions: 2, Errors: 2.
    cassell:beeriously cassell$

    View Slide

  204. View Slide

  205. 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.
    ..E 2 / 3 (67%)
    Time: 292 ms, Memory: 4.00MB
    There was 1 error:
    1) Beeriously\Tests\Unit\Domain\Recipe\RecipeNameTest::testToString
    Object of class Beeriously\Domain\Recipe\RecipeName could not be converted to string
    /app/src/Tests/Unit/Domain/Recipe/RecipeNameTest.php:27
    ERRORS!
    Tests: 35, Assertions: 78, Errors: 1.
    make: *** [unit] Error 2
    cassell:beeriously cassell$

    View Slide

  206. View Slide

  207. 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)

    View Slide

  208. View Slide

  209. View Slide

  210. BrewedOn
    Example

    View Slide

  211. View Slide

  212. View Slide

  213. View Slide

  214. Pounds
    Example

    View Slide

  215. View Slide

  216. View Slide

  217. View Slide

  218. $poundA->reduceBy($poundB);

    View Slide

  219. Eliminates Checking
    Value Objects

    View Slide

  220. View Slide

  221. Factories
    $weight = $factory->create(‘2 lbs’);

    View Slide

  222. Static
    Constructors

    View Slide

  223. Natural
    Language
    Constructors

    View Slide

  224. View Slide

  225. View Slide

  226. Temperature
    Example

    View Slide

  227. View Slide

  228. View Slide

  229. View Slide

  230. View Slide

  231. View Slide

  232. Composite
    Value Objects

    View Slide

  233. Full Name
    First Name + Last Name

    View Slide

  234. View Slide

  235. Encoding
    Business Logic in
    Value Objects

    View Slide

  236. Amount of
    Alcohol

    View Slide

  237. View Slide

  238. ABV

    View Slide

  239. Sugar + Yeast =
    Alcohol + CO2

    View Slide

  240. Sugar + Yeast =
    Alcohol + CO
    2
    C H O ->2C H OH + 2CO
    6 12 6 2 5 2

    View Slide

  241. Sugar + Yeast =
    Alcohol + CO2

    View Slide

  242. Sugar + Yeast =
    Alcohol + CO2

    View Slide

  243. Sugar - Sugar = Alcohol
    START FINISH

    View Slide

  244. Sugar - Sugar = Alcohol
    START FINISH
    Volume
    Volume

    View Slide

  245. Specific Gravity

    View Slide

  246. View Slide

  247. Hydrometer

    View Slide

  248. View Slide

  249. learntomoonshine.com

    View Slide

  250. Michael L. Hall - Zymurgy, Summer 1995, vol. 18, no. 2

    View Slide

  251. View Slide

  252. View Slide

  253. View Slide

  254. View Slide

  255. View Slide

  256. View Slide

  257. View Slide

  258. View Slide

  259. View Slide

  260. View Slide

  261. View Slide

  262. View Slide

  263. View Slide

  264. View Slide

  265. View Slide

  266. View Slide

  267. View Slide

  268. View Slide

  269. View Slide

  270. DDD CRUD/MUD
    RecipeName String
    Temperature Float(7,3)
    DateBrewed DateTime
    Gravity Float(7,3)
    ABV Function + Validation?

    View Slide

  271. DDD BALL OF MUD/CRUD
    Correctness ✓✓
    Testability ✓
    Usability ✓
    Maintainability
    Modifiability

    View Slide

  272. View Slide

  273. Entities

    View Slide

  274. Identifiable
    Have State and are Mutable (Lifecycle)
    Never in an Invalid State
    Operate using Value Objects
    No Security or Permission Checks
    *(Ideally) Storage Agnostic
    Entities

    View Slide

  275. DO AS MUCH AS
    YOU CAN IN
    VALUE OBJECTS!

    View Slide

  276. Doctrine Mappings
    Doctrine Annotations
    Laravel Eloquent ORM
    “Storage Agnostic”

    View Slide

  277. BEHAVIOR FIRST!
    STORAGE SECOND!
    “Storage Agnostic”

    View Slide

  278. Single Entity
    Entities
    Regular Aggregate
    Contain Other
    Entities

    View Slide

  279. Brewer
    Grain
    Hops
    BeerOnTap
    Entity Types
    Regular Aggregate
    Invoice
    Recipe
    BrewSession
    BeerMenu

    View Slide

  280. 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, …

    View Slide

  281. View Slide

  282. SETTERS
    ARE BAD

    View Slide

  283. View Slide

  284. View Slide

  285. View Slide

  286. View Slide

  287. Calling two setters in a row
    on the same object
    Missing a Concept?

    View Slide

  288. Calling two methods in a row
    on the same object
    Missing a Concept?

    View Slide

  289. Passing more than one
    parameter to a method
    Missing a Concept?

    View Slide

  290. View Slide

  291. View Slide

  292. Doctrine

    View Slide

  293. View Slide

  294. View Slide

  295. View Slide

  296. View Slide

  297. View Slide

  298. View Slide

  299. View Slide

  300. View Slide

  301. View Slide

  302. Bounded
    Contexts

    View Slide

  303. Inventory
    Recipes Brewhouse

    View Slide

  304. Inventory
    Recipes
    Brewhouse
    Recipe\Grain
    Inventory\Grain
    Brewing\BrewSession\Grain

    View Slide

  305. Inventory
    Recipes Brewhouse

    View Slide

  306. Inventory
    Recipes Brewhouse
    Brewers
    Supporting Bounded Context

    View Slide

  307. • Entities
    • Manages Child Entities
    • Transactional Boundary
    • The Easiest Models to Identify
    Aggregates

    View Slide

  308. Aggregates
    Recipe
    BrewSession
    BeerMenu

    View Slide

  309. View Slide

  310. View Slide

  311. View Slide

  312. View Slide

  313. View Slide

  314. View Slide

  315. View Slide

  316. View Slide

  317. View Slide

  318. DDD BALL OF MUD/CRUD
    Correctness ✓✓
    Testability ✓✓
    Usability ✓
    Maintainability ✓✓
    Modifiability ✓

    View Slide

  319. Task Based
    User Interface

    View Slide

  320. View Slide

  321. View Slide

  322. View Slide

  323. DDD BALL OF MUD/CRUD
    Correctness ✓✓
    Testability ✓✓
    Usability ✓✓
    Maintainability ✓✓
    Modifiability ✓

    View Slide

  324. View Slide

  325. Security

    View Slide

  326. Domain
    (Business Logic)
    Application
    Services
    Controller
    Persistence
    Event/Command Bus
    Security
    Templates

    View Slide

  327. Controller
    Domain
    Framework

    View Slide

  328. View Slide

  329. View Slide

  330. View Slide

  331. View Slide

  332. Controller
    Domain
    Framework

    View Slide

  333. Mathias Verraes - Decoupling the Model from the Framework at Laracon EU 2014
    https://www.youtube.com/watch?v=QaIGN_cTcc8

    View Slide

  334. Ruby Midwest 2011 - Keynote: Architecture the Lost Years by Robert Martin
    https://www.youtube.com/watch?v=WpkDN78P884

    View Slide

  335. View Slide

  336. View Slide

  337. Domain
    (Business Logic)
    Application
    Services
    Controller
    Persistence
    Event/Command Bus
    Security
    Templates

    View Slide

  338. DDD BALL OF MUD/CRUD
    Correctness ✓✓
    Testability ✓✓
    Usability ✓✓
    Maintainability ✓✓✓
    Modifiability ✓

    View Slide

  339. Domain
    Events

    View Slide

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

    View Slide

  341. View Slide

  342. BrewSessionWasPlanned

    View Slide

  343. BrewSessionWasPlanned

    View Slide

  344. View Slide

  345. View Slide

  346. View Slide

  347. View Slide

  348. View Slide

  349. View Slide

  350. View Slide

  351. DDD BALL OF MUD/CRUD
    Correctness ✓✓
    Testability ✓✓
    Usability ✓✓
    Maintainability ✓✓✓
    Modifiability ✓✓

    View Slide

  352. Business
    Rules in a
    Specification

    View Slide

  353. View Slide

  354. View Slide

  355. View Slide

  356. DDD BALL OF MUD/CRUD
    Correctness ✓✓✓
    Testability ✓✓
    Usability ✓✓
    Maintainability ✓✓✓
    Modifiability ✓✓

    View Slide

  357. View Slide

  358. Command Query
    Responsibility
    Segregation

    View Slide

  359. CQRS

    View Slide

  360. Write Read

    View Slide

  361. Write
    Model
    Read
    Model(s)

    View Slide

  362. Write
    Domain
    Model
    Read
    Model(s)

    View Slide

  363. Controller
    Command Bus
    Handler Handler Handler

    View Slide

  364. Controller
    Command Bus
    Handler Handler Handler
    Request

    View Slide

  365. Controller
    Command Bus
    Handler Handler Handler
    Command

    View Slide

  366. View Slide

  367. View Slide

  368. Controller
    Command Bus
    Handler Handler Handler
    Command

    View Slide

  369. Controller
    Command Bus
    Handler Handler Handler
    Command

    View Slide

  370. Controller
    Command Bus
    Handler Handler Handler
    Command

    View Slide

  371. Controller
    Command Bus
    Handler Handler Handler
    Response

    View Slide

  372. Controller
    Command Bus
    Handler Handler Handler
    Response

    View Slide

  373. Controller
    Command Bus
    Handler Handler Handler
    Response

    View Slide

  374. https://gnugat.github.io/
    2016/05/11/towards-cqrs-
    command-bus.html

    View Slide

  375. View Slide

  376. View Slide

  377. View Slide

  378. View Slide

  379. View Slide

  380. View Slide

  381. Why Do CQRS?

    View Slide

  382. Controller
    Domain
    Framework

    View Slide

  383. Controller
    API (versions?)
    Console Commands

    View Slide

  384. Event
    Sourcing

    View Slide

  385. View Slide

  386. View Slide

  387. View Slide

  388. Event Sourcing
    • Object Properties are Not Persisted
    • Events Are Persisted to 

    Append Only Event Storage

    View Slide

  389. • Avoids Data Mapping
    • Avoids Object-relational Impedance Mismatch
    • Reduces Database Table Counts (Related Tables)
    • Potentially Reduces Model Counts
    Event Sourcing

    View Slide

  390. Slide: Microsoft

    View Slide

  391. Libraries
    • https://github.com/broadway
    • https://github.com/prooph
    • https://github.com/szjani/predaddy

    View Slide

  392. Recipe
    Example

    View Slide

  393. Learning
    More

    View Slide

  394. homebrewersassociation.org

    View Slide

  395. View Slide

  396. View Slide

  397. DEV BOOK CLUB
    https://www.youtube.com/user/devbookclub

    View Slide

  398. View Slide

  399. View Slide

  400. View Slide

  401. View Slide

  402. View Slide

  403. DDDinPHP
    Google Group
    http://DDDinPHP.org

    View Slide

  404. Final Thoughts

    View Slide

  405. View Slide

  406. View Slide

  407. View Slide

  408. View Slide

  409. View Slide

  410. View Slide

  411. Inventory
    Recipes Brewhouse

    View Slide

  412. Inventory
    Recipes Brewhouse
    Legacy
    Context

    View Slide

  413. View Slide

  414. View Slide

  415. https://joind.in/talk/ee152
    DDD Topics Covered
    •Ubiquitous Language
    •Event Storming
    •Modelling
    •Value Objects
    •Entities
    •Aggregates
    •Hexagonal Architecture
    •CQRS
    •Event Sourcing

    View Slide