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

From CRUD to Hexagonal & DDD, how Meetic saved its codebase

From CRUD to Hexagonal & DDD, how Meetic saved its codebase

Frameworks give us a CRUD mindset, not suited to handle business complexity.
Discover patterns you can re-use on your project, that really helped us, and tips to spread a new culture amongst your colleagues.
Hosted by Polymorphic meetup group : https://www.meetup.com/fr-FR/Polymorphic/events/260342370/

Jean-Marie Lamodière

May 09, 2019
Tweet

More Decks by Jean-Marie Lamodière

Other Decks in Programming

Transcript

  1. From CRUD to Hexagonal & DDD “How Meetic saved its

    codebase” Icons made by Icongeek26, Freepik from www.flaticon.com Jean-Marie Lamodière - Backend tech lead @jmlamodiere
  2. But… Monolithic Rest API = new Legacy v2 Icons made

    by Freepik from www.flaticon.com
  3. What’s wrong ? We use a modern web framework !

    We follow its best practices ! Icons made by Freepik from www.flaticon.com
  4. WHY ? MVC Framework = CRUD Create Read Update Delete

    = Database mindset Icons made by Smashicon from www.flaticon.com
  5. WHY ? MVC Framework = CRUD Typical tutorial : 1.

    Generate entities + getters + setters 2. Generate Database 3. Expose as REST (or web <form>)
  6. WHY ? MVC Framework = CRUD N-tiers architecture Presentation Business

    logic Data access Framework focus Framework focus ...YOLO ! Icons made by Freepik, Smashicon from www.flaticon.com
  7. WHY ? Tests… with code coverage in mind Complex Class

    Icons made by Pause08, Freepik from www.flaticon.com = No refactoring Test coupled with internal details + 100%
  8. What is a useful test ? How to code business

    complexity ? Icons made by Freepik from www.flaticon.com
  9. WHY ? Let’s try buzzwords ! Test Driven Development Behaviour

    Driven Development Command Bus Hexagonal architecture Domain Driven Design Command Query Separation Icons made by Roundicons from www.flaticon.com Microservices
  10. WHY ? 3 years later... • Inconsistant code designs •

    Differing opinions amongst developers Fewer libraries! More libraries! Get sh*t done! Refactor everything! Fewer libraries! More libraries! Refactor everything!
  11. WHY ? Evangelism Pro-tips • Lead by example Pull Requests,

    Pair Programming • Organize debates Teach, show code, vote • Code example as documentation • Use (anti-)pattern names
  12. Hexagonal Architecture = Ports & Adapters Dr. Alistair Cockburn -

    2005 Hexagonal Architecture Command / Query Separation Domain Driven Design Testing Strategy Gotchas Pro-Tips
  13. WHY ?What is “Decoupling” ? Low coupling High cohesion “Allow

    unrelated things to change independently” Icons made by Roundicons from www.flaticon.com
  14. WHY ?Hexagonal architecture Low coupling High cohesion “Decoupling business and

    technical choices” Icons made by Roundicons from www.flaticon.com
  15. WHY ?Hexagonal architecture “Techno agnostic domain” Infrastructure • Framework (+

    version) • Libraries (ORM, http client) • Database • REST, SOAP, Command line ? Domain (Business logic)
  16. WHY ? Where to put my code ? Infrastructure Framework,

    libraries, techno Domain (Business logic) Icons made by Icongeek26 from www.flaticon.com Product Owner Business expert Architect Dev, Ops (Who decides ?)
  17. WHY ?Domain inside hexagon RepositoryInterface Entity No framework Icons made

    by Freepik from www.flaticon.com /Domain MemberRepository Member Screaming architecture
  18. WHY ? Port = simplest interface meeting use-case needs UseCase

    RepositoryInterface Entity Start inside hexagone, free your mind from infrastructure No framework Icons made by Freepik from www.flaticon.com
  19. WHY ? Port = simplest interface meeting use-case needs Icons

    made by Roundicons, Freepik from www.flaticon.com repo.persist(member).flush (framework details) repo.add(member) repo.get(id) A repository can be seen as a list
  20. WHY ? Port = simplest interface meeting use-case needs Icons

    made by Roundicons, Freepik from www.flaticon.com Intention revealing interface getInactiveMembers() getDefaultChoices(gender) increaseCounter()
  21. WHY ? Adapter = techno-specific “driver” UseCase RepositoryInterface Entity MysqlRepository

    HttpRepository No framework Framework welcome Icons made by Freepik from www.flaticon.com TestRepository
  22. WHY ? Adapter = techno-specific “driver” MysqlRepository Icons made by

    Freepik from www.flaticon.com /Domain MemberRepository Member /Infrastructure MysqlMemberRepo... Framework welcome
  23. WHY ? MysqlMemberRepository implements MemberRepositoryInterface { getInactiveMembers() {...} } Adapter

    = techno-specific “driver” Techno name Icons made by Freepik from www.flaticon.com
  24. WHY ? Adapter = techno-specific “driver” UseCase RepositoryInterface Entity MysqlRepository

    @Throws UserNotFound Icons made by Freepik from www.flaticon.com Convert infra exceptions to domain exceptions
  25. WHY ? Adapter = techno-specific “driver” UseCase RepositoryInterface Entity AggregatorRepository

    MysqlDao* CacheDecoratorDao HttpDao *Data Access Object (No impact here) Icons made by Freepik from www.flaticon.com (Complex infra)
  26. WHY ? Difference with n-tiers architecture Service MysqlDao1* Dto1* Dao2Interface

    *Data Access Object *Data Transfert Object CacheDecoratorDao2 HttpDao2 Dto2 (Aware of 2 Dao) (Mimic database structure)
  27. WHY ? Port & Adapters : entrypoints UseCase RepositoryInterface Entity

    RestController Test CliController No framework Framework welcome Icons made by Freepik from www.flaticon.com
  28. RestController Framework welcome Icons made by Freepik from www.flaticon.com /Infrastructure

    /Symfony MemberController MemberSerializer WHY ? Port & Adapters : entrypoints
  29. WHY ? Ex. of adapter refacto @Meetic Symfony 2 Database

    Database Oracle Icons made by Freepik from www.flaticon.com Symfony 4 External REST API Config files PostgreSQL
  30. WHY ?Layers inside hexagone UseCase RepositoryInterface Entity Icons made by

    Freepik from www.flaticon.com Application Use cases (do, get…) Domain Modeling business reality (Use case agnostic)
  31. Command Query Separation Command (do) : produces an effect Query

    (get) : gets data, no state change Same as “Query” vs. “Mutation” in GraphQL Hexagonal Architecture Command / Query Separation Domain Driven Design Testing Strategy Gotchas Pro-Tips
  32. WHY ? Command / Query Separation RepositoryInterface Entity Icons made

    by Freepik, Smashicons from www.flaticon.com Query Handler Command Handler
  33. WHY ? Command / Query Separation /Application /Command RegisterMember RegisterMemberHandler

    /Query GetNewMembers GetNewMembersHandler /Domain MemberRepository Member
  34. WHY ? Command / Query Separation Command, Query • simple

    DTO • user intention • primitive values /Application /Command RegisterMember RegisterMemberHandler /Query GetNewMembers GetNewMembersHandler
  35. WHY ? Steps 1. Infrastructure\Controller Converts HTTP request to Command/Query

    and call Handler 2. Application\CommandHandler Apply logic, act on Entity through Repository 3. Infrastructure\Controller Send HTTP response
  36. WHY ? Command / Query Separation Command Handler RepositoryInterface Entity

    Icons made by Freepik, Smashicons from www.flaticon.com Query Handler DaoInterface DTO (with validation) (no logic, readonly) Same database (≠ CQRS)
  37. WHY ? Command / Query Separation /Application /Command RegisterMember RegisterMemberHandler

    /Query GetContactPictures GetContactPicturesHandler /Domain MemberRepository Member /Read ContactDao ContactPicture
  38. WHY ? Command / Query Separation Create custom Read DAO

    only when it simplifies code Ex : custom DTO with less fields
  39. Domain Driven Design Eric Evans - 2004 Icons made by

    Icongeek26, Freepik from www.flaticon.com Hexagonal Architecture Command / Query Separation Domain Driven Design Testing Strategy Gotchas Pro-Tips
  40. WHY ? Domain Driven Design • Talk with domain experts

    • Use domain words in your code • Focus on events instead of data Icons made by Icongeek26 from www.flaticon.com
  41. WHY ? Before DDD : fake entities... Anemic domain model

    Temporal coupling memberEntity .setStatus(“active”) .setActivationDate(now) .setCanTalk(true) Icons made by Roundicons from www.flaticon.com Feature envy
  42. WHY ? Same (anti)patterns with REST PUT /member/123 {“status”: “active”,

    “activDate”:”now”“canTalk”:true} Icons made by Roundicons, Freepik from www.flaticon.com POST /member/123 {“action”: “activate”} Logic on caller = feature envy POST /member/123/activate
  43. WHY ? DDD Entity Rich domain model Icons made by

    Roundicons from www.flaticon.com memberEntity .activate() • State always valid (check invariants) • Explicit mutators • Emit domain events
  44. WHY ? Before DDD : primitive types only... Icons made

    by Roundicons from www.flaticon.com Primitive obsession productEntity .setPriceAmount(20.50) .setCurrenty(“EUR”) (same concern)
  45. WHY ? DDD Value Object price = new Price(20.50, “EUR”)

    productEntity .setPrice(price) • Explicit • Immutable • Valid
  46. WHY ? DDD Value Object @ Meetic age = new

    Age(23) … = age .toSearchBirthdayMax() • Factorize validation • Factorize features Must be >= 18
  47. Testing strategy Hexagonal Architecture Command / Query Separation Domain Driven

    Design Testing Strategy Gotchas Pro-Tips Icons made by Freepik from www.flaticon.com
  48. WHY ? Inside hexagon: unit tests Command Handler RepositoryInterface Entity

    Icons made by Freepik, Smashicons from www.flaticon.com • Mock Infra only • Don’t mock entities / value objects Test Mock
  49. WHY ? Outside hexagon: integration tests Icons made by Freepik,

    Smashicons from www.flaticon.com Don’t mock a library API The test should allow refactoring : • QueryBuilder -> SQL • Change Http library MysqlRepository Docker mysql HttpRepository Wiremock
  50. Gotchas ? Pro-Tips ? Hexagonal Architecture Command / Query Separation

    Domain Driven Design Testing Strategy Gotchas Pro-Tips Icons made by Freepik from www.flaticon.com
  51. WHY ? Do not stack new layers Lava flow Refacto

    “vertical” business slices Icons made by Freepik, Pixel Perfect, Roundicons from www.flaticon.com New layer Old layer 1 Old layer 2
  52. WHY ? Gotchas, Pro-tips • Hexagon overkill when no domain

    logic. CRUD / framework magic more suited. • Start inside hexagon, not with database. Otherwise, infra complexity will leak into domain • To choose layer => whose decision is it ? Product Owner Business expert Architect Dev, Ops @jmlamodiere Icons made by Icongeek26 from www.flaticon.com Thanks for coming !