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

From CRUD to DDD: How did we end up here? - Dut...

From CRUD to DDD: How did we end up here? - Dutch PHP Conference 2024

So you’ve moved from a legacy database-centric architecture to a brand new Domain-Driven Design approach. Your coworker told you that this would be about speaking the language of the customer and putting the primary focus on the business domain logic. But now you find yourself looking at aggregate roots, command buses, event projects and process managers, and wonder what they have to do with that. Moreover, what used to be an easy change now requires editing at least seven different files. How did we end up here?

In this talk we will look at an evolution from CRUD to a modern architecture based on DDD and adjacent patterns like CQRS and Event Sourcing. We will follow the thought process that led to each of these new tactical constructs, see which problems they solve, but also which disadvantages they have. This talk is not a criticism of DDD, but aims to show where it and related patterns shine (and where they don’t), so you can make a deliberate choice where and when to apply them and develop to their strengths.

Arnout Boks

March 15, 2024
Tweet

More Decks by Arnout Boks

Other Decks in Programming

Transcript

  1. From CRUD to DDD How did we end up here?

    Arnout Boks - Dutch PHP Conference - 16 March 2024
  2. CRUD The database has a central role. The interface (both

    backend API and UI) are just a thin wrapper.
  3. CRUD User Interface edit_recipe.php view_recipe.php Database SQL SQL id: 123

    id: 123 title: “foo” ingredients: [...] published: true
  4. CRUD with a modern twist User Interface Recipe:edit controller Recipe:view

    controller Database SQL GET /recipes/123 POST /recipes/123 title: “foo” ingredients: [...] published: true app.php front controller ORM
  5. Domain Driven Design (DDD) The domain (the subject area where

    the software is used) and the business logic therein gets a central role in the application. The rest of the software (the infrastructure) is built around it.
  6. Domain Driven Design: ‘the full monty’ User Interface Recipe:publish controller

    Database app.php front controller Recipe:view controller Command bus PublishRecipe command PublishRecipe command handler Recipe aggregate Recipe write model repository RecipePublished event Event store Event bus SQL Recipe event projector ORM Recipe read model repository SQL load & save publish
  7. Before we start • Steps may be taken in different

    order • Steps may be executed differently ◦ Interpretation of principles ◦ Implementation choices ◦ Chosen technology (language, framework, ORM)
  8. Before we start New! New! New! Generic, system-wide component Component

    specific to this feature/use case New possibility, optional addition
  9. DDD as an umbrella term • Ubiquitous language • Event

    Sourcing • Command pattern • Entity • CQRS • Repository • Aggregate • Value Object • Event-driven architecture • Bounded context • Command bus • Anti-corruption layer
  10. Recap: our CRUD starting point User Interface Recipe:edit controller Database

    app.php front controller Recipe:view controller ORM SQL
  11. Aggregates An aggregate is a cluster of domain objects that

    can be treated as a single unit. An aggregate is responsible for maintaining the integrity and business rules within the cluster.
  12. Aggregates User Interface Recipe:edit controller Database app.php front controller Recipe:view

    controller Recipe aggregate Recipe repository ORM SQL load & save edit title: “foo” ingredients: [...] published: true
  13. Aggregates User Interface Recipe:edit controller Database app.php front controller Recipe:view

    controller Recipe aggregate Recipe repository ORM SQL Unit tests load & save edit title: “foo” ingredients: [...] published: true
  14. Command pattern (1) User Interface Recipe:publish controller Database app.php front

    controller Recipe:view controller Recipe aggregate Recipe repository ORM SQL load & save publish
  15. Command pattern (1) User Interface Recipe:publish controller Database app.php front

    controller Recipe:view controller Recipe aggregate Recipe repository ORM SQL Recipe:addIngredient controller load & save publish
  16. Command pattern (2) User Interface Recipe:publish controller Database app.php front

    controller Recipe:view controller PublishRecipe command PublishRecipe command handler Recipe aggregate Recipe repository ORM SQL publish load & save
  17. Command pattern (2) User Interface Recipe:publish controller Database app.php front

    controller Recipe:view controller PublishRecipe command PublishRecipe command handler Recipe aggregate Recipe repository ORM SQL recipe:publish console command publish load & save
  18. Command bus A ‘one stop shop’ to execute commands. The

    command bus will find the correct handler for a command and deliver the command to it.
  19. Command bus User Interface Recipe:publish controller Database app.php front controller

    Recipe:view controller Command bus PublishRecipe command PublishRecipe command handler Recipe aggregate Recipe repository ORM SQL
  20. Command bus decoration Command bus command handler 2 command handler

    1 command handler 3 controller 2 controller 1 console command 1
  21. Command bus decoration Transactional command bus Command bus command handler

    2 command handler 1 command handler 3 controller 2 controller 1 console command 1
  22. Queueing command bus Logging command bus Command bus decoration Transactional

    command bus Command bus command handler 2 command handler 1 command handler 3 controller 2 controller 1 console command 1
  23. Command bus User Interface Recipe:publish controller Database app.php front controller

    Recipe:view controller Command bus PublishRecipe command PublishRecipe command handler Recipe aggregate Recipe repository ORM SQL
  24. Command-Query Responsibility Separation CQRS distinguishes commands (which change the state

    of the system) from queries (which retrieve information, but make no changes) and treats these separately. In particular, this means that both responsibilities can have separate (optimized/specialized) models.
  25. Command-Query Responsibility Separation User Interface Recipe:publish controller Database app.php front

    controller Recipe:view controller Command bus PublishRecipe command PublishRecipe command handler Recipe aggregate Recipe write model repository ORM Recipe read model repository SQL ?
  26. Command-Query Responsibility Separation User Interface Recipe:publish controller Database app.php front

    controller Recipe:view controller Command bus PublishRecipe command PublishRecipe command handler Recipe aggregate Recipe write model repository ORM Recipe read model repository SQL Recipe:dashboard controller Recipe dashboard read model repository ? Redis cluster
  27. Event-Driven Architecture An Event-Driven Architecture uses events (models of significant

    things that happened in the past) for the communication between, and decoupling of, components in the system.
  28. Event-Driven Architecture User Interface Recipe:publish controller Database app.php front controller

    Recipe:view controller Command bus PublishRecipe command PublishRecipe command handler Recipe aggregate Recipe write model repository RecipePublished event Event bus Recipe event projector ORM Recipe read model repository SQL
  29. Event-Driven Architecture User Interface Recipe:publish controller Database app.php front controller

    Recipe:view controller Command bus PublishRecipe command PublishRecipe command handler Recipe aggregate Recipe write model repository RecipePublished event Event bus Recipe event projector ORM Recipe read model repository SQL Notifications event listener ResetRatings process manager
  30. Event Sourcing Events are stored, and are the single source

    of truth in the system. All other information is derived from the events.
  31. Event Sourcing: role of the aggregate PublishRecipe command Recipe aggregate

    RecipePublished event RecipeIngredientAdded event ingredient: Onion quantity: 1 RecipeIngredientAdded event ingredient: Garlic quantity: 2 cloves
  32. Event Sourcing: role of the aggregate PublishRecipe command Recipe aggregate

    HasNoIngredients exception RecipeIngredientAdded event ingredient: Onion quantity: 1 RecipeIngredientRemoved event ingredient: Onion
  33. Event Sourcing: role of the aggregate PublishRecipe command Recipe aggregate

    AlreadyPublished exception RecipeIngredientAdded event ingredient: Onion quantity: 1 RecipeIngredientAdded event ingredient: Garlic quantity: 2 cloves RecipePublished event
  34. Event Sourcing User Interface Recipe:publish controller Database app.php front controller

    Recipe:view controller Command bus PublishRecipe command PublishRecipe command handler Recipe aggregate Recipe write model repository RecipePublished event Event store Event bus SQL Recipe event projector ORM Recipe read model repository SQL load & save events
  35. Reprojection User Interface Recipe:publish controller Database app.php front controller Recipe:view

    controller Command bus PublishRecipe command PublishRecipe command handler Recipe aggregate Recipe write model repository RecipePublished event Event store Event bus SQL Recipe event projector ORM Recipe read model repository SQL Reprojection system Audit dashboard load & save events
  36. Domain Driven Design: ‘the full monty’ User Interface Recipe:publish controller

    Database app.php front controller Recipe:view controller Command bus PublishRecipe command PublishRecipe command handler Recipe aggregate Recipe write model repository RecipePublished event Event store Event bus SQL Recipe event projector ORM Recipe read model repository SQL publish load & save
  37. What did we gain? • Business domain logic at the

    core of our code • Decoupling • Testability • Commands conveying clear intention • Command transactionality • Command logging • Command queueing • Specialized read models • Specialized data stores • Managers for distributed business processes • Notifications • Auditability • Reprojections for data migrations and new read models
  38. Thank you! - Any questions? Arnout Boks @[email protected] @arnoutboks https://aboks.github.io

    https://www.moxio.com Feedback https://joind.in/talk/565f8 We’re hiring!