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

Immutability to save an ever-changing world

Immutability to save an ever-changing world

Want to build software that is more testable, easier to modify, and has fewer lines of code? Architecting with more immutable objects that are always in a valid state is the most important lesson I have learned in building better software applications. Using immutable value objects will lead to less checking, fewer bugs, and more DRY code, and will help avoid the “spooky action at a distance” problem in PHP. We will also learn how to use immutable objects and immutable collections to improve design of our mutable entities. Lastly, we’ll see how immutable objects and functional programming can reduce complexity.

Andrew Cassell

October 16, 2018
Tweet

More Decks by Andrew Cassell

Other Decks in Programming

Transcript

  1. Andrew Cassell @alc277 andrewcassell.com
    Immutability to Save an
    Ever-Changing World

    View Slide

  2. View Slide

  3. View Slide

  4. View Slide

  5. Immutability &
    Invariants &
    Encapsulation &
    Behavior

    View Slide

  6. Good
    Code?

    View Slide

  7. View Slide

  8. View Slide

  9. $car->bark();
    $dog->drive();
    It was not supposed to be about cars barking or dogs driving

    View Slide

  10. View Slide

  11. BEER

    View Slide

  12. Immutability &
    Invariants &
    Encapsulation &
    Behavior

    View Slide

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

    View Slide

  14. View Slide

  15. View Slide

  16. View Slide

  17. View Slide

  18. View Slide

  19. View Slide

  20. View Slide

  21. View Slide

  22. View Slide

  23. Passed in by
    Reference /
    Pointer

    View Slide

  24. View Slide

  25. View Slide

  26. View Slide

  27. View Slide

  28. Spooky
    Action at a
    Distance

    View Slide

  29. View Slide

  30. View Slide

  31. View Slide

  32. View Slide

  33. View Slide

  34. View Slide

  35. Mutable objects
    depend on time.

    View Slide

  36. Mutable = Value(t)
    Immutable = Value()

    View Slide

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

    View Slide

  38. View Slide

  39. What should be
    Immutable?

    View Slide

  40. Event

    View Slide

  41. Event History

    View Slide

  42. Request

    View Slide

  43. Response

    View Slide

  44. Command

    View Slide

  45. Almost
    Everything

    View Slide

  46. HOW?

    View Slide

  47. Immutability
    in PHP

    View Slide

  48. Plain Old PHP Objects
    (POPOs)

    View Slide

  49. • POPOs
    • No setters, all parameters are passed
    into constructor
    • Declare all class properties as private
    • No properties that are mutable objects
    Immutable Objects in PHP

    View Slide

  50. View Slide

  51. View Slide

  52. View Slide

  53. View Slide

  54. View Slide

  55. Composite
    Immutable
    Objects

    View Slide

  56. View Slide

  57. View Slide

  58. View Slide

  59. View Slide

  60. View Slide

  61. View Slide

  62. View Slide

  63. View Slide

  64. View Slide

  65. Reflection
    API

    View Slide

  66. More
    Classes &
    Files

    View Slide

  67. Performance &
    Memory Usage

    View Slide

  68. Immutability &
    Invariants &
    Encapsulation &
    Behavior

    View Slide

  69. Immutability &
    Invariants &
    Encapsulation &
    Behavior

    View Slide

  70. OBJECTS FOR
    ALL THE THINGS

    View Slide

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

    View Slide

  72. Value
    Objects

    View Slide

  73. View Slide

  74. • POPOs
    • No setters, all parameters are passed
    into constructor
    • Declare all class properties as private
    • No properties that are mutable objects
    Immutable Objects in PHP

    View Slide

  75. • Immutable Objects
    • Always in a valid state
    • Validate in constructor
    • Behavioral methods return new object
    • Not dependencies
    Value Objects in PHP

    View Slide

  76. Always Valid
    Value Object
    Immutable
    No Identity (Only Values)
    Recipe Name
    Weight
    Temperature
    Entity
    Identifiable
    Mutable
    Lifecycle
    Contains Value Objects
    Line Item
    Brewer
    Recipe

    View Slide

  77. First Name
    Last Name
    Full Name
    Email Address
    Recipe Name
    Date Brewed On
    Recipe ID
    Temperature
    Liters
    Gallons
    Kilograms
    Pounds
    Value Objects

    View Slide

  78. Value Objects

    View Slide

  79. View Slide

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

    View Slide

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

    View Slide

  82. RecipeName
    Example

    View Slide

  83. View Slide

  84. View Slide

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

  86. View Slide

  87. View Slide

  88. View Slide

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

  90. View Slide

  91. BrewedOn
    Example

    View Slide

  92. View Slide

  93. Pounds
    Example

    View Slide

  94. View Slide

  95. View Slide

  96. View Slide

  97. 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 == null); // true (string)
    var_dump(-0.0 == "negativezero"); // true (string)

    View Slide

  98. Immutability &
    Invariants &
    Encapsulation &
    Behavior

    View Slide

  99. Eliminate Checking
    Value Objects

    View Slide

  100. View Slide

  101. View Slide

  102. View Slide

  103. View Slide

  104. View Slide

  105. Static
    Constructors

    View Slide

  106. Natural
    Language
    Constructors

    View Slide

  107. View Slide

  108. View Slide

  109. Immutability &
    Invariants &
    Encapsulation &
    Behavior

    View Slide

  110. View Slide

  111. $poundsA->reduceBy($poundsB);

    View Slide

  112. if(!is_numeric($a)) {
    throw new \Exception(…)
    }
    if(!is_numeric($b)) {
    throw new \Exception(…)
    }
    if($a < $b) {
    throw new \Exception(…)
    }
    $pounds = $a - $b;

    View Slide

  113. Don’t Extend

    View Slide

  114. class Pounds extends FloatValue
    {
    //…
    }

    View Slide

  115. class EmailAddress extends StringObject
    {
    //…
    }

    View Slide

  116. View Slide

  117. View Slide

  118. Composite
    Value Objects

    View Slide

  119. Immutability &
    Invariants &
    Encapsulation &
    Behavior

    View Slide

  120. Full Name
    First Name + Last Name

    View Slide

  121. View Slide

  122. Full Name
    Salutation + First Name
    Middle Name(s) +
    Last Name + Suffix

    View Slide

  123. Encoding
    Business Logic in
    Value Objects

    View Slide

  124. Alcohol by
    Volume

    View Slide

  125. View Slide

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

    View Slide

  127. Sugar + Yeast =
    Alcohol + CO2

    View Slide

  128. Sugar - Sugar = Alcohol
    START FINISH

    View Slide

  129. Sugar - Sugar = Alcohol
    START FINISH
    Volume
    Volume

    View Slide

  130. Hydrometer

    View Slide

  131. learntomoonshine.com

    View Slide

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

    View Slide

  133. View Slide

  134. Calibration Temperature
    Minimum Gravity Reading
    Maximum Gravity Reading

    View Slide

  135. View Slide

  136. View Slide

  137. View Slide

  138. View Slide

  139. 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. View Slide

  150. View Slide

  151. View Slide

  152. View Slide

  153. View Slide

  154. View Slide

  155. View Slide

  156. View Slide

  157. View Slide

  158. View Slide

  159. Immutable Value Objects Typical
    RecipeName String
    Temperature Float(7,3)
    DateBrewed DateTime
    Gravity Float(7,3)
    ABV Function + Validation?

    View Slide

  160. Immutability &
    Invariants &
    Encapsulation &
    Behavior

    View Slide

  161. Entities

    View Slide

  162. Identifiable
    Have State and are Mutable (Lifecycle)
    Operate using Value Objects
    *(Ideally) Storage Agnostic
    *(Ideally) Never in an Invalid State
    Entities

    View Slide

  163. View Slide

  164. View Slide

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

    View Slide

  166. SETTERS
    ARE BAD

    View Slide

  167. View Slide

  168. View Slide

  169. View Slide

  170. View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  174. View Slide

  175. View Slide

  176. View Slide

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

    View Slide

  178. View Slide

  179. Doctrine

    View Slide

  180. View Slide

  181. View Slide

  182. View Slide

  183. View Slide

  184. View Slide

  185. View Slide

  186. View Slide

  187. View Slide

  188. What is actually
    an entity?

    View Slide

  189. View Slide

  190. View Slide

  191. View Slide

  192. View Slide

  193. View Slide

  194. View Slide

  195. View Slide

  196. Immutable
    “Entity”

    View Slide

  197. Immutable
    Book
    Mutable
    Price

    View Slide

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

    View Slide

  199. $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

    View Slide

  200. Immutable
    Collections

    View Slide

  201. View Slide

  202. View Slide

  203. View Slide

  204. Value Object

    View Slide

  205. View Slide

  206. “Immutable”
    Collection
    of Entities

    View Slide

  207. View Slide

  208. View Slide

  209. View Slide

  210. View Slide

  211. View Slide

  212. View Slide

  213. View Slide

  214. View Slide

  215. View Slide

  216. View Slide

  217. View Slide

  218. Functional
    Programming

    View Slide

  219. Procedural
    Programming

    View Slide

  220. $recipe = new Recipe(//…);
    $recipe->addThisIngredient(//…);
    $recipe->addWater(//…);
    $recipe->addThatIngredient(//…);
    $recipe->boil(//…);
    $recipe->getEstimatedABV();
    //… === inputs

    View Slide

  221. $recipe = new Recipe($time + //…);
    $recipe->addThisIngredient($time + //…);
    $recipe->addWater($time + //…);
    $recipe->addThatIngredient($time + //…);
    $recipe->boil($time + //…);
    $recipe->getEstimatedABV($time);

    View Slide

  222. $recipe = new Recipe($history + //…);
    $recipe->addThisIngredient($history + //…);
    $recipe->addWater($history + //…);
    $recipe->addThatIngredient($history + //…);
    $recipe->boil($history + //…);
    $recipe->getEstimatedABV($history);

    View Slide

  223. Functional
    Programming

    View Slide

  224. View Slide

  225. View Slide

  226. Functional programming with
    mutable objects is more difficult
    than with immutable objects.

    View Slide

  227. Don’t Do It.

    View Slide

  228. Functional
    Programming?

    View Slide

  229. Procedural with
    Immutable
    Objects

    View Slide

  230. Objected Oriented
    Programming with
    Immutable Objects

    View Slide

  231. Final Thoughts

    View Slide

  232. View Slide

  233. homebrewersassociation.org

    View Slide

  234. View Slide

  235. PHP BEER BREWING SOFTWARE FOR
    BITTER DEVELOPERS WHO WANT TO
    BREW EVEN MORE BITTER BEERS
    And never heard of not-invented-here and wanna
    learn and do other good stuff too.

    View Slide

  236. View Slide

  237. View Slide

  238. View Slide

  239. https://www.youtube.com/watch?v=O3T9-FilHVI

    View Slide

  240. View Slide

  241. Think
    Immutable
    First

    View Slide

  242. https://joind.in/talk/aabc8
    Thank You!
    @alc277

    View Slide