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

F4bb45b2a18ee44c4a28b1664de150bd?s=128

Andrew Cassell

May 21, 2020
Tweet

Transcript

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

    Andrew Cassell @alc277 andrewcassell.com
  2. available on

  3. 1 About CURIOSITYSTREAM • Founded by John Hendricks, founder of

    the Discovery Channel • Thousands of documentaries and series • HD and 4K • Available worldwide
  4. 4 WE ARE HIRING! https://jobs.daxx.com/browse-jobs/?search_keywords=curiositystream

  5. None
  6. None
  7. None
  8. github.com/cassell

  9. BEER

  10. Good Code?

  11. Testable SOLID Less Defensive

  12. None
  13. None
  14. Monolith Microservices Serverless

  15. None
  16. None
  17. None
  18. None
  19. Domain-Driven Design

  20. Domain-Driven Design

  21. Common Sense Software Development

  22. None
  23. None
  24. None
  25. None
  26. 26

  27. 27

  28. None
  29. $car->bark(); $dog->drive();

  30. None
  31. Encapsulation Modeling Behavior

  32. Domain

  33. available on

  34. BEER

  35. None
  36. Photo: Kathleen Pierce - Bangor Daily News

  37. None
  38. None
  39. None
  40. None
  41. None
  42. None
  43. Brewing Software

  44. None
  45. None
  46. None
  47. None
  48. recipe hop grain hop_inventory grain_inventory recipe_hop_link user recipe_user_link brew_event ROLE_USER

    ROLE_ADMIN
  49. Source: http://jonclaytonbiz.com/

  50. None
  51. None
  52. People don't want to buy a quarter-inch drill, they want

    a quarter- inch hole. Theodore Levitt
  53. None
  54. None
  55. None
  56. #JBTD (http://jobstobedone.org/)

  57. None
  58. YYYYY why? why? why? why? why?

  59. Why? Why? Why? Why? Why?

  60. Search Caluclation List Automation

  61. DDD !=

  62. None
  63. None
  64. None
  65. None
  66. None
  67. http://www.csharpstar.com/

  68. None
  69. DDD CRUD Correctness Testability Usability Maintainability Modifiability

  70. None
  71. DDD BALL OF MUD / CRUD Correctness Testability Usability Maintainability

    Modifiability
  72. DDD CRUD/MUD Recipe Building Inventory Brewhouse Activity Beer Menu /

    Prices Brew Sessions Brewery Website Calclations QA Record Keeping
  73. Who is responsible

  74. None
  75. None
  76. None
  77. None
  78. Ubiquitous Language

  79. Ubiquitous Language Developers Domain Experts

  80. None
  81. Ubiquitous Language

  82. User Brewer Employee Taster Customer

  83. None
  84. 1. Naming Things 2. Cache Invalidation 3. Off By One

    Errors Top 10 Reasons Programming is Hard
  85. $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();
  86. None
  87. Ubiquitous Language $recipe->addGrains($grains); Code: We need to be able to

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

    calculate an estimated ABV for a recipe. Business:
  89. 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.
  90. 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:
  91. $recipe->dryHop($hop); $recipe->addIngredient($hop); $recipe->addToBoil($hop); $recipe->addToBoilKettle($hop); $recipe->addToBoiler($hop); $recipe->addFirstWortHop($hop); $recipe->addToFermenter($hop);

  92. $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());
  93. $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());
  94. CRUD/MUD DDD Users Brewers String RecipeName Integer IBU Float(7,3) AlphaAcid

    Array of ORM Objects GrainBill BeerInventoryRepositoryInterface Menu
  95. […], 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.
  96. DDD BALL OF MUD/CRUD Correctness ✓ Testability Usability Maintainability Modifiability

  97. None
  98. The Brewer, designed and engraved in the Sixteenth Century, by

    Jost Amman.
  99. Inventory Marketing Finance Production “Hop”

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

  101. Bounded Contexts

  102. Inventory Recipes Brewhouse

  103. Slide: Vaughn Vernon

  104. None
  105. Inventory Recipes Brewhouse

  106. Inventory Recipes Brewhouse

  107. Message Inventory Recipes Brewhouse

  108. Domain Event Inventory Recipes Brewhouse

  109. None
  110. None
  111. None
  112. None
  113. None
  114. Personas

  115. adaptivepath.com

  116. 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.
  117. None
  118. None
  119. None
  120. None
  121. https://blog.intercom.io/using-job-stories-design-features-ui-ux/

  122. None
  123. None
  124. 7 Dirty Words When Meeting With a Domain Expert 1.Session

    2.Repository 3.Abstract 4.Interface 5.Class 6.Database 7.Foreign Key
  125. User Research

  126. Photo: Mathias Verraes

  127. None
  128. Command Actor Model Domain Event View Whatever

  129. Domain Event • Something that happens in the software. •

    Past Tense
  130. Domain Event • Beer Was Brewed • Grain Was Added

    • Hops Were Added • Brewer Changed Email Address
  131. None
  132. None
  133. None
  134. None
  135. None
  136. None
  137. None
  138. None
  139. None
  140. None
  141. https://bit.ly/2LL1Xib

  142. Command Actor Model Domain Event

  143. Command • The action that precipitates the event • Written

    in Present Tense
  144. Command • Start Brewing Session • Add Grain to Recipe

    • Add Hops To Fermenter • Change Email Address
  145. None
  146. None
  147. None
  148. Command Actor Model Domain Event

  149. Model • Command is acted upon this thing • Might

    Include Supporting Models
  150. Model • Recipe • Ingredient • Brewer

  151. Command Actor Model Domain Event

  152. Actor • Who did it? • If you only have

    one user you might not need this
  153. None
  154. None
  155. Water profiles change daily

  156. Command Actor Model Domain Event

  157. None
  158. None
  159. None
  160. Source: Jeff Patton

  161. None
  162. business origami

  163. Jess McMullin Business Origami

  164. Jess McMullin Business Origami

  165. Sean Jalleh

  166. None
  167. DDD BALL OF MUD/CRUD Correctness ✓✓ Testability Usability ✓ Maintainability

    Modifiability
  168. Domain Model

  169. Domain Objects Value Objects Domain Events Entities Aggregates

  170. 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
  171. OBJECTS FOR ALL THE THINGS

  172. Value Objects

  173. Email Address Recipe Name Date We Brewed On Weight Temperature

    Value Objects
  174. Value Objects Email Address Recipe Name Date We Brewed On

    Weight Temperature Ubiquitous Language
  175. Value Objects EmailAddress RecipeName BrewedOn Pounds DegreesFahrenheit Email Address Recipe

    Name Date We Brewed On Weight Temperature
  176. Plain Old PHP Objects (POPOs) Value Objects

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

  179. None
  180. None
  181. None
  182. None
  183. None
  184. None
  185. None
  186. None
  187. None
  188. None
  189. Passed in by Reference / Pointer

  190. None
  191. None
  192. None
  193. None
  194. Spooky Action at a Distance

  195. None
  196. None
  197. None
  198. None
  199. None
  200. None
  201. Mutable objects depend on state.

  202. None
  203. Mutable objects depend on time.

  204. $mutable->function(a,b,c) { global $time; //… } $immutable->function(a,b,c) { //… }

  205. $mutable->getValue() { global $time; return $value($time); } $immutable->getValue() { return

    $value; }
  206. 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.
  207. Immutability

  208. Function

  209. value Mutable

  210. 42 Mutated

  211. value Immutable

  212. value Immutable

  213. value Immutable

  214. value Immutable

  215. Mutable Immutable Read-Only After Creation Read & Write

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

  217. None
  218. None
  219. IMMUTABLE AND ALWAYS VALID Value Objects

  220. Value Objects

  221. None
  222. “Gateway Drug to Test Driven Development” Value Objects

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

  224. 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
  225. Only depend on scalars or other value objects. Value Objects

  226. RecipeName Example

  227. None
  228. None
  229. None
  230. 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.
  231. None
  232. None
  233. 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)
  234. None
  235. None
  236. BrewedOn Example

  237. None
  238. None
  239. None
  240. Pounds Example

  241. None
  242. <?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)
  243. None
  244. $poundA->reduceBy($poundB);

  245. Eliminate Checking Value Objects

  246. None
  247. None
  248. None
  249. None
  250. What should be and Immutable Value Object?

  251. Event

  252. Event History

  253. Request

  254. Response

  255. Almost Everything

  256. Natural Language Constructors

  257. Natural Language Factory Methods

  258. Static Constructors

  259. None
  260. None
  261. Resist the Urge to Extend

  262. class Pounds extends FloatValue { //… }

  263. class EmailAddress extends StringObject { //… }

  264. None
  265. None
  266. Temperature Example

  267. None
  268. None
  269. None
  270. None
  271. None
  272. Composite Value Objects

  273. Composite Value Objects

  274. None
  275. None
  276. None
  277. None
  278. None
  279. None
  280. None
  281. Encoding Business Logic in Value Objects

  282. Alcohol by Volume

  283. None
  284. Sugar + Yeast = Alcohol + CO 2 C H

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

  286. Sugar - Sugar = Alcohol START FINISH

  287. Sugar - Sugar = Alcohol START FINISH Volume Volume

  288. Hydrometer

  289. learntomoonshine.com

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

    2
  291. None
  292. Calibration Temperature Minimum Gravity Reading Maximum Gravity Reading

  293. None
  294. None
  295. None
  296. None
  297. None
  298. None
  299. None
  300. None
  301. None
  302. None
  303. None
  304. None
  305. None
  306. None
  307. None
  308. None
  309. None
  310. None
  311. None
  312. None
  313. None
  314. None
  315. None
  316. Как тебе такое, Илон Маск?

  317. None
  318. None
  319. None
  320. DDD CRUD/MUD RecipeName String Temperature Float(7,3) DateBrewed DateTime Gravity Float(7,3)

    ABV Function + Validation?
  321. DDD BALL OF MUD/CRUD Correctness ✓✓ Testability ✓ Usability ✓

    Maintainability Modifiability
  322. Domain Entities

  323. None
  324. Entities Identifiable Have State and are Mutable (Lifecycle) Operate using

    Value Objects *(Ideally) Storage Agnostic *(Ideally) Never in an Invalid State
  325. DO AS MUCH AS YOU CAN IN VALUE OBJECTS!

  326. Doctrine Zend Hydrator *Laravel Eloquent ORM “Storage Agnostic”

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

  328. Single Entity Entities Regular Aggregate Contain Other Entities

  329. Brewer Grain Hops BeerOnTap Entity Types Regular Aggregate Invoice Recipe

    BrewSession BeerMenu
  330. 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, …
  331. None
  332. None
  333. SETTERS ARE BAD

  334. None
  335. None
  336. None
  337. None
  338. Calling two setters in a row on the same object

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

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

    Concept?
  341. None
  342. None
  343. Doctrine

  344. None
  345. None
  346. None
  347. None
  348. None
  349. None
  350. None
  351. None
  352. None
  353. None
  354. None
  355. None
  356. None
  357. None
  358. None
  359. None
  360. Bounded Contexts

  361. Inventory Recipes Brewhouse

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

  363. Inventory Recipes Brewhouse

  364. Inventory Recipes Brewhouse Brewers Supporting Bounded Context

  365. Mathias Verraes - Mathias Verraes - Emergent Boundaries https://www.youtube.com/watch?v=ECM1rPYxvD4

  366. • Entities • Manages Child Entities • Transactional Boundary •

    The Easiest Models to Identify Aggregates
  367. Aggregates Recipe BrewSession BeerMenu

  368. None
  369. None
  370. None
  371. None
  372. None
  373. None
  374. Immutable “Entity”

  375. None
  376. None
  377. None
  378. None
  379. None
  380. None
  381. Immutable Book Mutable Price

  382. $book->setPrice(16.96); $price = new Price($book,15.00) $price->setAmount(16.96); Mutable Immutable

  383. $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
  384. Immutable Collections

  385. npm install immutable

  386. <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>
  387. None
  388. None
  389. Value Object

  390. None
  391. “Immutable” Collection of Entities

  392. None
  393. None
  394. None
  395. None
  396. None
  397. None
  398. None
  399. $recipe->addGrain() $recipe->removeGrain() $recipe->updateGrain()

  400. $object->create(x) $object->read(x) $object->update(x) $object->delete(x)

  401. $object->create(x) { // validate x }

  402. $object->read(x) { // validate x }

  403. $object->update(x) { // validate x }

  404. $object->delete(x) { // validate x }

  405. None
  406. None
  407. None
  408. None
  409. Validate Once Not Validating Transitions Easier to Define the Rules

    Fewer Moving Parts
  410. None
  411. None
  412. Entity’s job is to manage state.

  413. DDD BALL OF MUD/CRUD Correctness ✓✓ Testability ✓✓ Usability ✓

    Maintainability ✓✓ Modifiability ✓
  414. Task Based User Interface

  415. None
  416. None
  417. None
  418. DDD BALL OF MUD/CRUD Correctness ✓✓ Testability ✓✓ Usability ✓✓

    Maintainability ✓✓ Modifiability ✓
  419. None
  420. Security

  421. None
  422. Domain (Business Logic) Application Services Controller Persistence Event/Command Bus Security

    Templates
  423. Controller Domain Module

  424. Controller Domain Package

  425. Controller Domain Framework

  426. None
  427. None
  428. None
  429. None
  430. Controller Domain Framework

  431. Mathias Verraes - Decoupling the Model from the Framework at

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

    Robert Martin https://www.youtube.com/watch?v=WpkDN78P884
  433. None
  434. Domain (Business Logic) Application Services Controller Persistence Event/Command Bus Security

    Templates
  435. DDD BALL OF MUD/CRUD Correctness ✓✓ Testability ✓✓ Usability ✓✓

    Maintainability ✓✓✓ Modifiability ✓
  436. Domain Events

  437. 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
  438. None
  439. BrewSessionWasPlanned

  440. BrewSessionWasPlanned

  441. None
  442. None
  443. None
  444. None
  445. None
  446. None
  447. None
  448. Inventory Recipes Brewhouse

  449. Inventory Recipes Brewhouse

  450. Domain Event Inventory Recipes Brewhouse

  451. Domain Event Inventory Recipes Brewhouse

  452. Inventory Recipes Brewhouse Legacy Context

  453. Inventory Recipes Brewhouse Legacy Context

  454. Inventory Recipes Brewhouse Legacy Context

  455. Inventory Recipes Brewhouse Anticorruption Layer Legacy Context Legacy Code Legacy

    Services
  456. DDD BALL OF MUD/CRUD Correctness ✓✓✓ Testability ✓✓ Usability ✓✓

    Maintainability ✓✓✓ Modifiability ✓✓
  457. Specifications Repositories Factories Fetch Build/Create Filter/Evaluate Services Do Things

  458. None
  459. (EXCEPT CACHING!)

  460. SPECIFICATION

  461. Specifications $spec->isSatisfiedBy($object);

  462. None
  463. None
  464. None
  465. Example MinimumDiastaticPowerSpecification

  466. None
  467. CQRS

  468. Command Query Responsibility Segregation (Command–query separation)

  469. Write Read

  470. Write Model Read Model(s)

  471. Write Domain Model Read Model(s)

  472. Write Domain Model Read (Complicated SQL Queries)

  473. Controller Command Bus Handler Handler Handler

  474. Controller Command Bus Handler Handler Handler Request

  475. Controller Command Bus Handler Handler Handler Command

  476. None
  477. None
  478. Controller Command Bus Handler Handler Handler Command

  479. Controller Command Bus Handler Handler Handler Command

  480. Controller Command Bus Handler Handler Handler Command

  481. Controller Command Bus Handler Handler Handler Command Response

  482. Controller Command Bus Handler Handler Handler Command

  483. Controller Command Bus Handler Handler Handler Response

  484. Controller Command Bus Handler Handler Handler Response

  485. Controller Command Bus Handler Handler Handler Response

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

  487. None
  488. Why Do CQRS?

  489. Controller Domain Framework

  490. Controller API (versions?) Console Commands

  491. Command Bus Command Query Responsibility Segregation

  492. Write Domain Model Read Model(s)

  493. DDD BALL OF MUD/CRUD Correctness ✓✓✓ Testability ✓✓ Usability ✓✓

    Maintainability ✓✓✓✓ Modifiability ✓✓✓
  494. Event Sourcing

  495. None
  496. None
  497. None
  498. Event Sourcing • Object Properties are Not Persisted • Events

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

    (Related Tables) • Potentially Reduces Model Counts Event Sourcing
  500. Slide: Microsoft

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

  502. Recipe Example

  503. 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
  504. Recipe Example

  505. Learning More

  506. homebrewersassociation.org

  507. None
  508. None
  509. DEV BOOK CLUB https://www.youtube.com/user/devbookclub

  510. None
  511. None
  512. https://idddworkshop.com/

  513. None
  514. None
  515. DDDinPHP Google Group http://DDDinPHP.org

  516. None
  517. Final Thoughts

  518. None
  519. None
  520. None
  521. None
  522. None
  523. None
  524. DDD Topics Covered •Ubiquitous Language •Event Storming •Modelling •Value Objects

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