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

Writing cleaner code with Domain-Driven Design

Writing cleaner code with Domain-Driven Design

I always thought that my code was fine. I took my time for naming things, wrote small classes/methods and tried to use other Clean Code practices. But after a job interview with a technical coach, I was pointed towards Domain-Driven Design (DDD) and some other design topics. I could learn a lot from it, said the technical coach. And I did! Since then, I started noticing how much parts of DDD could help with my day to day job. I will take you with me on my learning journey of Domain-Driven Design, and how it helped me improve my code. We will touch upon themes like Ubiquitous Language, Supple Design, Bounded Contexts and more. You can expect a talk with a lot of code examples and practical advise that you can use the next day.

Paul van der Slot

May 13, 2022
Tweet

More Decks by Paul van der Slot

Other Decks in Programming

Transcript

  1. What is DDD? 11 Aneamic domain model Data vs behaviour

    Tell don’t Ask How to split a bigger system
  2. BLOG: Domain-Driven Design in 2020, by Alberto Brandolini Approach to

    software development • Focus on learning What is DDD? 14
  3. BLOG: Domain-Driven Design in 2020, by Alberto Brandolini Approach to

    software development • Focus on learning • Language as a first class citizen What is DDD? 15
  4. BLOG: Domain-Driven Design in 2020, by Alberto Brandolini Approach to

    software development • Focus on learning • Language as a first class citizen • Multiple models is the key for not evolving into a big ball of mud. What is DDD? 16
  5. BLOG: Domain-Driven Design in 2020, by Alberto Brandolini Approach to

    software development • Focus on learning • Language as a first class citizen • Multiple models is the key for not evolving into a big ball of mud. • Good coding practices are expected. What is DDD? 17
  6. BLOG: Domain-Driven Design in 2020, by Alberto Brandolini Approach to

    software development • Focus on learning • Language as a first class citizen • Multiple models is the key for not evolving into a big ball of mud. • Good coding practices are expected. • Know your core domain What is DDD? 18
  7. BLOG: Domain-Driven Design in 2020, by Alberto Brandolini • Focus

    on learning • Language as a first class citizen • Multiple models is the key for not evolving into a big ball of mud. • Good coding practices are expected. • Know your core domain What is DDD? 19
  8. BLOG: Domain-Driven Design in 2020, by Alberto Brandolini • Focus

    on learning • Language as a first class citizen • Multiple models is the key for not evolving into a big ball of mud. • Good coding practices are expected. • Know your core domain What is DDD? 20
  9. 21

  10. Who am I Paul van der Slot • Freelance Software

    Engineer • Fan of a lot of software practices and techniques like: DDD, Collaborative Design, Clean Code, Hexagonal Architecture, Refactoring, Agile, XP, TDD, Pair Programming, and way more 22
  11. 25 Ubiquitous Language Scrum Team Code Test Scenarios User Stories

    Domain experts specific for a domain Ubiquitous Language
  12. 26 Ubiquitous Language Scrum Team Code Test Scenarios User Stories

    Domain experts specific for a domain Ubiquitous Language
  13. 29 Code Domain model Ubiquitous Language customer payment invoice product

    customer payment invoice product DB Caching REST Monitoring
  14. 30 Code Domain model Ubiquitous Language customer payment invoice product

    customer payment invoice product DB Caching REST Monitoring Domain logic matching with the business problem Separate technical details from the domain logic As little as possible accidental complexity in domain
  15. public class CardService { private CardDetailsRestClient restclient; private CardsRepository cardRepository;

    void activateCards(List<CardId> cardIds){ List<RestCardDetails> cardDetails = restClient.getExtraCardInformation(cardIds); List<CardDbEntity> dbCards = cardRepository.getCards(cardIds); activateCards(dbCards, cardDetails); } } 33
  16. public class CardService { private CardDetailsRestClient restclient; private CardsRepository cardRepository;

    void activateCards(List<CardId> cardIds){ List<RestCardDetails> cardDetails = restClient.getExtraCardInformation(cardIds); List<CardDbEntity> dbCards = cardRepository.getCards(cardIds); activateCards(dbCards, cardDetails); } private List<Card> activateCards(List<CardDbEntity> dbCards, List<RestCardDetails> cardDetails){ // do validation / transformation / null checks of external data // business logic updatedDbCards.forEach(cardRepository::save); } } 34
  17. public class CardService { private CardsRepository cardRepository; void activateCards(List<CardId> cardIds){

    List<Card> cards = cardRepository.findAll(cardIds); activateCards(cards); } private void activateCards(List<Card> cards){ // business logic updatedCards.forEach(cardRepository::save); } } 35 public class CardService { private CardDetailsRestClient restclient; private CardsRepository cardRepository; void activateCards(List<CardId> cardIds){ List<RestCardDetails> cardDetails = restClient.getExtraCardInformation(cardIds); List<CardDbEntity> dbCards = cardRepository.getCards(cardIds); activateCards(dbCards, cardDetails); } private List<Card> activateCards(List<CardDbEntity> dbCards, List<RestCardDetails> cardDetails){ // do validation / transformation / null checks of external data // business logic updatedDbCards.forEach(cardRepository::save); } }
  18. public class CardService { private CardsRepository cardRepository; void activateCards(List<CardId> cardIds){

    List<Card> cards = cardRepository.findAll(cardIds); activateCards(cards); } private void activateCards(List<Card> cards){ // business logic updatedCards.forEach(cardRepository::save); } } interface CardsRepository { // port List<Card> findAll(List<CardId> cardId); void save(Card card); } 36 public class CardService { private CardDetailsRestClient restclient; private CardsRepository cardRepository; void activateCards(List<CardId> cardIds){ List<RestCardDetails> cardDetails = restClient.getExtraCardInformation(cardIds); List<CardDbEntity> dbCards = cardRepository.getCards(cardIds); activateCards(dbCards, cardDetails); } private List<Card> activateCards(List<CardDbEntity> dbCards, List<RestCardDetails> cardDetails){ // do validation / transformation / null checks of external data // business logic updatedDbCards.forEach(cardRepository::save); } }
  19. public class CardService { private CardsRepository cardRepository; void activateCards(List<CardId> cardIds){

    List<Card> cards = cardRepository.findAll(cardIds); activateCards(cards); } private void activateCards(List<Card> cards){ // business logic updatedCards.forEach(cardRepository::save); } } interface CardsRepository { // port List<Card> findAll(List<CardId> cardId); void save(Card card); } // Outside business logic, in an adapter class MongoCardRepository implements CardsRepository { // do validation / transformation / null checks } 37 public class CardService { private CardDetailsRestClient restclient; private CardsRepository cardRepository; void activateCards(List<CardId> cardIds){ List<RestCardDetails> cardDetails = restClient.getExtraCardInformation(cardIds); List<CardDbEntity> dbCards = cardRepository.getCards(cardIds); activateCards(dbCards, cardDetails); } private List<Card> activateCards(List<CardDbEntity> dbCards, List<RestCardDetails> cardDetails){ // do validation / transformation / null checks of external data // business logic updatedDbCards.forEach(cardRepository::save); } }
  20. 38

  21. 40

  22. Value Object 41 A Value Object is an immutable type

    that is distinguishable only by the state of its properties. Examples: Height, Money, AccountNumber, Color
  23. Value Object 42 A Value Object is an immutable type

    that is distinguishable only by the state of its properties. Examples: Height, Money, AccountNumber, Color Using new Types (Value Objects) to make your code more explicit
  24. 43 void checkIn(int roomNumber){ validatePositive(roomNumber); int floorNumber = determineFloor(roomNumber); //

    do check in logic } int determineFloor(int roomNumber){ validatePositive(roomNumber); // do logic } Dan Bergh Johnsson - The Power of Value
  25. 45 void checkIn(RoomNumber roomNumber){ Floor floor = roomNumber.getFloor(); // do

    check in logic } • Validation inside the Value Object • Limit operations to useful ones. RoomNumber is not an int! No + - * / • Place to put logic relevant to a Domain Concept, like getFloor()
  26. Value object vs Util method 47 Email Validation duplicated around

    the codebase boolean matchesPattern(String inputValue){ Pattern p = Pattern .compile(" ^[a-zA-Z0-9_!#$%&’*+/=?`{|}~^.-]+@[a-zA-Z0-9.-]+$ "); Matcher m = p.matcher(inputValue); return m.matches(); }
  27. Value object vs Util method 48 • Call the Util

    when you need to validate • You need to know the UTIL exists (not forcing) • You don’t know if the email is validated yet, later on in the code EmailUtil.matchesPattern(emailAddress); Option 1: Put it in a Utillity class
  28. Value object vs Util method 49 • Everywhere, you use

    the validated Email • Email type as a parameter later on (type safe) • Explicit domain meaning Email email = new Email(emailAddress); Option 2: Create a Value Object
  29. From Stringly typed / primitive obsession to domain primitives Talk

    with your team and introduce a ubiquitous language 50
  30. From Stringly typed / primitive obsession to domain primitives learning

    concepts -> introduce in code current understanding -> reflected in code 52
  31. From Stringly typed / primitive obsession to domain primitives learning

    concepts -> introduce in code current understanding -> reflected in code change in code -> verify with business and change shared model 53
  32. From Stringly typed / primitive obsession to domain primitives learning

    concepts -> introduce in code current understanding -> reflected in code change in code -> verify with business and change shared model iterative process 54
  33. 55

  34. TELL DON’T ASK 57 Tell an object what to do

    instead of asking for internal data to do it yourself This invites you to add the behavior in the object the data belongs to!
  35. TELL DON’T ASK 58 public boolean canBeActivated(Card card) { Account

    account = card.getAccount(); if(account.isBlocked()){ return false; } Customer owner = card.getOwner(); if(owner != null && isTrustworthy(owner)){ return false; } return true; }
  36. GETTERS AND SETTERS ARE EVIL 61 patient.setShotType(ShotTypes.TYPE_FLU); patient.setDose(dose); patient.setNurse(nurse); Vaccine

    vaccine = vaccines. standardAdultFluDose(); nurse.administerFluVaccine(patient,vaccine);
  37. GETTERS AND SETTERS ARE EVIL 62 patient.setShotType(ShotTypes.TYPE_FLU); patient.setDose(dose); patient.setNurse(nurse); …

    Customer owner = card.getOwner(); if(owner != null && isTrustworthy(owner)){ return false; } … Vaccine vaccine = vaccines. standardAdultFluDose(); nurse.administerFluVaccine(patient,vaccine); card.canBeActivated();
  38. GETTERS AND SETTERS ARE EVIL 63 Setters – I haven’t

    seen a good example. Better to name it like the business action Getters - Do I really need that information outside this object? Don’t autogenerate getters and setters, and think twice before you introduce them
  39. What is Supple Design? Supple, as the opposite of stiff

    Loosely coupled Highly cohesive Able to change Supple Design 65
  40. What is Supple Design? Supple, as the opposite of stiff

    Loosely coupled Highly cohesive Able to change Supple Design 66 Heuristics and Patterns to get to Supple Design
  41. COUPLING Supple Design 69 Coupling has to do with change

    Coupling(a,b,∆) = a∆ -> b∆ for the total correctness of the system Tie Together
  42. COUPLING Supple Design 71 Degree of Coupling Billing Service Item

    Tight Coupling Loose Coupling double price = item.getGrossPrice(); TaxRate taxRate = item.getTaxRate(); return price * (1 + (taxRate.percentage() / 100)); Billing Service Item return item.computeNetPrice(); TaxRate TaxRate Tie Together
  43. COHESION Supple Design 72 High Cohesion Low Cohesion When one

    thing changes, all has to change Stick Together
  44. COHESION Supple Design 73 Low Cohesion When one thing changes,

    all has to change. Isn’t that bad? What is the alternative? “Attempting to divide a cohesive class would only result in increased coupling and decreased readability” High Cohesion Stick Together
  45. COHESION Supple Design 74 Low Cohesion When one thing changes,

    all has to change Isn’t that bad? What is the alternative? Cohesion has a lot to do with terms like: • Single Responsibility Principle (SRP) • Separation of Concerns (SoC) High Cohesion Stick Together
  46. LOW COUPLING & HIGH COHESION Supple Design 75 Effect on

    software: • Testability increases (single purpose / less dependencies)
  47. LOW COUPLING & HIGH COHESION Supple Design 76 Effect on

    software: • Testability increases (single purpose / less dependencies) • Maintainability increases / Impact of change decreases (changes are in one place)
  48. LOW COUPLING & HIGH COHESION Supple Design 77 Effect on

    software: • Testability increases (single purpose / less dependencies) • Maintainability increases / Impact of change decreases (changes are in one place) • Reusability increases (Units with a single purpose)
  49. SUPPLE DESIGN 79 Intention revealing interfaces Side effect free functions

    Supple Design Heuristics and Patterns to get to Supple Design
  50. INTENTION REVEALING INTERFACES When a method name isn’t clear, you

    have to look inside the method to see what it is for. Or inside the next method it is calling. Or inside the next method it is calling. Or inside the next method it is calling. 80 Supple Design
  51. INTENTION REVEALING INTERFACES - Group things that belong together (easier

    to name) - Name things really well 81 Supple Design
  52. INTENTION REVEALING INTERFACES - Group things that belong together (easier

    to name) - Name things really well chef.prepare(String string) 82 Supple Design
  53. INTENTION REVEALING INTERFACES - Group things that belong together (easier

    to name) - Name things really well chef.prepare(String string) chef.make(Dish dish) 83 Supple Design
  54. private Collection<PriceDetails> totalOrderPrice; private double taxRate; PriceDetails calculatePriceDetails(Item item, int

    amount) { PriceDetails priceDetails = PriceDetails.of(item, amount, taxRate); totalOrderPrice.add(priceDetails); return priceDetails; } 84 SIDE EFFECT FREE FUNCTIONS Supple Design
  55. SIDE EFFECT FREE FUNCTIONS private Collection<PriceDetails> totalOrderPrice; private double taxRate;

    PriceDetails calculatePriceDetails(Item item, int amount) { PriceDetails priceDetails = PriceDetails.of(item, amount, taxRate); totalOrderPrice.add(priceDetails); return priceDetails; } 85 Supple Design
  56. SIDE EFFECT FREE FUNCTIONS Why? Can lead to unexpected behavior

    and is therefore the cause of many bugs WRITE PURE FUNCTIONS WHERE POSSIBLE! 87 Supple Design
  57. SIDE EFFECT FREE FUNCTIONS But side effects can’t be prevented

    • Updating a Database • Calling a Webservice • Sending a message 88 Supple Design
  58. SIDE EFFECT FREE FUNCTIONS But side effects can’t be prevented

    • Updating a Database • Calling a Webservice • Sending a message 89 Note: This mostly happens at the edge of your application. In the domain logic, pure functions can be used a lot Supple Design
  59. 90 Imperative Shell Functional Core Inside your business logic: •

    Majority is pure functional code FUNCTIONAL CORE, IMPERATIVE SHELL
  60. 91 Imperative Shell Functional Core Inside your business logic: •

    Majority is pure functional code • Code with side-effects in the imperative shell FUNCTIONAL CORE, IMPERATIVE SHELL
  61. 92 Imperative Shell Functional Core Inside your business logic: •

    Majority is pure functional code • Code with side-effects in the imperative shell FUNCTIONAL CORE, IMPERATIVE SHELL Direction of dependencies
  62. 100 Different groups had different views (models) IT consultancy Employee

    - Project - Rate - Level - IBAN - Address - Completed Trainings - Ambitions Bounded Context
  63. 101 Different groups had different views (models) IT consultancy Employee

    - Project - Rate - Level - IBAN - Address - Completed Trainings - Ambitions Sales Finance People management HR Bounded Context
  64. 103 - The boundary within a domain where a particular

    domain model applies BOUNDED CONTEXT Bounded Context
  65. 104 - The boundary within a domain where a particular

    domain model applies - Language driven - Ubiquitous Language is unique per bounded context BOUNDED CONTEXT Bounded Context
  66. 105 - The boundary within a domain where a particular

    domain model applies - Language driven - Ubiquitous Language is unique per bounded context - A natural way to split up your model - Strictly consistent within your bounderies - Don´t be distracted or confused by issues outside BOUNDED CONTEXT Bounded Context
  67. 106 Different groups had different views (models) IT consultancy Employee

    - Project - Rate - Level - IBAN - Address - Completed Trainings - Ambitions Sales Finance People management HR Bounded Context
  68. 114 Autonomy -> Able to evolve alone Bounded Context Reduced

    coupling more important across bounded contexts
  69. 115 Autonomy -> Able to evolve alone Decreased cognitive load

    -> Only have to think about own bounded context Bounded Context Reduced coupling more important across bounded contexts
  70. 116 Autonomy -> Able to evolve alone Decreased cognitive load

    -> Only have to think about own bounded context Especially when Bounded Context = Team Bounded Context Reduced coupling more important across bounded contexts
  71. 118 Employee - IBAN - Address - Completed Trainings -

    Ambitions - Project - Rate - Level Finance People management Sales Is the CEO an employee / consultant? Bounded Context
  72. 123 “I don’t write clean code from the start, I

    don’t think anyone could” Uncle Bob REFACTOR What’s next?
  73. 124 “The moment that the code works, is when you

    start investing in your career” REFACTOR What’s next?