Save 37% off PRO during our Black Friday Sale! »

php[world] 2017 Domain-Driven Design Workshop

F4bb45b2a18ee44c4a28b1664de150bd?s=47 Andrew Cassell
November 15, 2017
190

php[world] 2017 Domain-Driven Design Workshop

F4bb45b2a18ee44c4a28b1664de150bd?s=128

Andrew Cassell

November 15, 2017
Tweet

Transcript

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

  2. None
  3. None
  4. None
  5. None
  6. None
  7. None
  8. Domain-Driven Design

  9. Domain-Driven Design

  10. None
  11. Common Sense Software Development

  12. None
  13. None
  14. None
  15. None
  16. None
  17. None
  18. None
  19. None
  20. 20

  21. None
  22. Encapsulation & Immutability & Modeling & Behavior

  23. Domain

  24. Domain

  25. BEER

  26. None
  27. None
  28. None
  29. None
  30. Photo: Kathleen Pierce - Bangor Daily News

  31. None
  32. None
  33. None
  34. None
  35. None
  36. None
  37. None
  38. None
  39. None
  40. None
  41. None
  42. Brewing Software

  43. None
  44. None
  45. None
  46. None
  47. None
  48. None
  49. None
  50. None
  51. None
  52. None
  53. None
  54. recipe hop grain hop_inventory grain_inventory recipe_hop_link user recipe_user_link brew_event ROLE_USER

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

  56. None
  57. None
  58. People don't want to buy a quarter-inch drill, they want

    a quarter-inch hole. Theodore Levitt
  59. None
  60. None
  61. None
  62. #JBTD (http://jobstobedone.org/)

  63. None
  64. YYYYY

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

  66. Search Caluclation List Automation

  67. DDD !=

  68. None
  69. None
  70. None
  71. None
  72. None
  73. http://www.csharpstar.com/

  74. None
  75. DDD CRUD Correctness Testability Usability Maintainability Modifiability

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

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

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

  80. None
  81. None
  82. None
  83. None
  84. Ubiquitous Language

  85. Ubiquitous Language Developers Domain Experts

  86. Ubiquitous Language

  87. User Brewer Employee Taster Customer

  88. None
  89. 1. Naming Things 2. Cache Invalidation 3. Off By One

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

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

    calculate an estimated ABV for a recipe. Business:
  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.
  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:
  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“
  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

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

    Array of ORM Objects GrainBill BeerInventoryRepositoryInterface Menu
  101. None
  102. None
  103. None
  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());
  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.
  106. DDD BALL OF MUD/CRUD Correctness ✓ Testability Usability Maintainability Modifiability

  107. None
  108. The Brewer, designed and engraved in the Sixteenth Century, by

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

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

  111. Bounded Contexts

  112. Inventory Recipes Brewhouse

  113. Slide: Vaughn Vernon

  114. None
  115. Inventory Recipes Brewhouse

  116. Inventory Recipes Brewhouse

  117. Message Inventory Recipes Brewhouse

  118. Domain Event Inventory Recipes Brewhouse

  119. None
  120. None
  121. None
  122. None
  123. None
  124. Personas

  125. adaptivepath.com

  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.
  127. None
  128. None
  129. None
  130. None
  131. https://blog.intercom.io/using-job-stories-design-features-ui-ux/

  132. None
  133. None
  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
  135. User Research

  136. Photo: Mathias Verraes

  137. Command Actor Model Domain Event

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

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

    • Hops Were Added • Brewer Changed Email Address
  140. None
  141. None
  142. None
  143. None
  144. None
  145. None
  146. None
  147. None
  148. None
  149. Command Actor Model Domain Event

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

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

    • Add Hops To Fermenter • Change Email Address
  152. None
  153. None
  154. Command Actor Model Domain Event

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

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

  157. None
  158. Command Actor Model Domain Event

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

    one user you might not need this
  160. None
  161. None
  162. Command Actor Model Domain Event

  163. None
  164. None
  165. business origami

  166. Jess McMullin Business Origami

  167. Jess McMullin Business Origami

  168. Jess McMullin Business Origami

  169. Sean Jalleh

  170. None
  171. None
  172. Source: Jeff Patton

  173. None
  174. None
  175. None
  176. None
  177. None
  178. DDD BALL OF MUD/CRUD Correctness ✓✓ Testability Usability ✓ Maintainability

    Modifiability
  179. Domain Objects Value Objects Domain Events Entities Aggregates

  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
  181. Value Objects

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

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

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

    Name Date We Brewed On Weight Temperature
  185. Not Just Static Typing https://github.com/Fiedzia/type-system-research/blob/master/README.md Value Objects

  186. Immutable

  187. Immutable Avoid Spooky Action at a Distance

  188. ALWAYS VALID Value Objects

  189. Value Objects

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

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

  192. Plain PHP Objects (POPOs) Value Objects

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

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

  196. RecipeName Example

  197. None
  198. None
  199. None
  200. None
  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.
  202. None
  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$
  204. None
  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$
  206. None
  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)
  208. None
  209. None
  210. BrewedOn Example

  211. None
  212. None
  213. None
  214. Pounds Example

  215. None
  216. None
  217. None
  218. $poundA->reduceBy($poundB);

  219. Eliminates Checking Value Objects

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

  222. Static Constructors

  223. Natural Language Constructors

  224. None
  225. None
  226. Temperature Example

  227. None
  228. None
  229. None
  230. None
  231. None
  232. Composite Value Objects

  233. Full Name First Name + Last Name

  234. None
  235. Encoding Business Logic in Value Objects

  236. Amount of Alcohol

  237. None
  238. ABV

  239. Sugar + Yeast = Alcohol + CO2

  240. Sugar + Yeast = Alcohol + CO 2 C H

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

  242. Sugar + Yeast = Alcohol + CO2

  243. Sugar - Sugar = Alcohol START FINISH

  244. Sugar - Sugar = Alcohol START FINISH Volume Volume

  245. Specific Gravity

  246. None
  247. Hydrometer

  248. None
  249. learntomoonshine.com

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

    2
  251. None
  252. None
  253. None
  254. None
  255. None
  256. None
  257. None
  258. None
  259. None
  260. None
  261. None
  262. None
  263. None
  264. None
  265. None
  266. None
  267. None
  268. None
  269. None
  270. DDD CRUD/MUD RecipeName String Temperature Float(7,3) DateBrewed DateTime Gravity Float(7,3)

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

    Maintainability Modifiability
  272. None
  273. Entities

  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
  275. DO AS MUCH AS YOU CAN IN VALUE OBJECTS!

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

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

  278. Single Entity Entities Regular Aggregate Contain Other Entities

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

    BrewSession BeerMenu
  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, …
  281. None
  282. SETTERS ARE BAD

  283. None
  284. None
  285. None
  286. None
  287. Calling two setters in a row on the same object

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

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

    Concept?
  290. None
  291. None
  292. Doctrine

  293. None
  294. None
  295. None
  296. None
  297. None
  298. None
  299. None
  300. None
  301. None
  302. Bounded Contexts

  303. Inventory Recipes Brewhouse

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

  305. Inventory Recipes Brewhouse

  306. Inventory Recipes Brewhouse Brewers Supporting Bounded Context

  307. • Entities • Manages Child Entities • Transactional Boundary •

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

  309. None
  310. None
  311. None
  312. None
  313. None
  314. None
  315. None
  316. None
  317. None
  318. DDD BALL OF MUD/CRUD Correctness ✓✓ Testability ✓✓ Usability ✓

    Maintainability ✓✓ Modifiability ✓
  319. Task Based User Interface

  320. None
  321. None
  322. None
  323. DDD BALL OF MUD/CRUD Correctness ✓✓ Testability ✓✓ Usability ✓✓

    Maintainability ✓✓ Modifiability ✓
  324. None
  325. Security

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

    Templates
  327. Controller Domain Framework

  328. None
  329. None
  330. None
  331. None
  332. Controller Domain Framework

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

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

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

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

    Maintainability ✓✓✓ Modifiability ✓
  339. Domain Events

  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
  341. None
  342. BrewSessionWasPlanned

  343. BrewSessionWasPlanned

  344. None
  345. None
  346. None
  347. None
  348. None
  349. None
  350. None
  351. DDD BALL OF MUD/CRUD Correctness ✓✓ Testability ✓✓ Usability ✓✓

    Maintainability ✓✓✓ Modifiability ✓✓
  352. Business Rules in a Specification

  353. None
  354. None
  355. None
  356. DDD BALL OF MUD/CRUD Correctness ✓✓✓ Testability ✓✓ Usability ✓✓

    Maintainability ✓✓✓ Modifiability ✓✓
  357. None
  358. Command Query Responsibility Segregation

  359. CQRS

  360. Write Read

  361. Write Model Read Model(s)

  362. Write Domain Model Read Model(s)

  363. Controller Command Bus Handler Handler Handler

  364. Controller Command Bus Handler Handler Handler Request

  365. Controller Command Bus Handler Handler Handler Command

  366. None
  367. None
  368. Controller Command Bus Handler Handler Handler Command

  369. Controller Command Bus Handler Handler Handler Command

  370. Controller Command Bus Handler Handler Handler Command

  371. Controller Command Bus Handler Handler Handler Response

  372. Controller Command Bus Handler Handler Handler Response

  373. Controller Command Bus Handler Handler Handler Response

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

  375. None
  376. None
  377. None
  378. None
  379. None
  380. None
  381. Why Do CQRS?

  382. Controller Domain Framework

  383. Controller API (versions?) Console Commands

  384. Event Sourcing

  385. None
  386. None
  387. None
  388. Event Sourcing • Object Properties are Not Persisted • Events

    Are Persisted to 
 Append Only Event Storage
  389. • Avoids Data Mapping • Avoids Object-relational Impedance Mismatch •

    Reduces Database Table Counts (Related Tables) • Potentially Reduces Model Counts Event Sourcing
  390. Slide: Microsoft

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

  392. Recipe Example

  393. Learning More

  394. homebrewersassociation.org

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

  398. None
  399. None
  400. None
  401. None
  402. None
  403. DDDinPHP Google Group http://DDDinPHP.org

  404. Final Thoughts

  405. None
  406. None
  407. None
  408. None
  409. None
  410. None
  411. Inventory Recipes Brewhouse

  412. Inventory Recipes Brewhouse Legacy Context

  413. None
  414. None
  415. https://joind.in/talk/ee152 DDD Topics Covered •Ubiquitous Language •Event Storming •Modelling •Value

    Objects •Entities •Aggregates •Hexagonal Architecture •CQRS •Event Sourcing