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/

64822af642dacc617380f009e35a78f7?s=128

Jean-Marie Lamodière

May 09, 2019
Tweet

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. stackshare.io/meetic @meetictech microservice calls Kafka messages production deployments 3 500

    000 000 250 000 000 25 / day
  3. Legacy REST API Buzzwords Evangelism 2002-2012 2013 2015-2018 2018

  4. WHY ? n-tiers architecture Presentation (Apps) Presentation Business logic Data

    access
  5. But… Monolithic Rest API = new Legacy v2 Icons made

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

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

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

    Generate entities + getters + setters 2. Generate Database 3. Expose as REST (or web <form>)
  9. 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
  10. 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%
  11. What is a useful test ? How to code business

    complexity ? Icons made by Freepik from www.flaticon.com
  12. Legacy REST API Buzzwords Evangelism 2002-2012 2013 2015-2018 2018

  13. 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
  14. 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!
  15. Legacy REST API Buzzwords Evangelism 2002-2012 2013 2015-2018 2018

  16. 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
  17. Hexagonal Architecture = Ports & Adapters Dr. Alistair Cockburn -

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

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

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

    version) • Libraries (ORM, http client) • Database • REST, SOAP, Command line ? Domain (Business logic)
  21. 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 ?)
  22. WHY ?Domain inside hexagon RepositoryInterface Entity No framework Icons made

    by Freepik from www.flaticon.com /Domain MemberRepository Member Screaming architecture
  23. 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
  24. 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
  25. WHY ? Port = simplest interface meeting use-case needs Icons

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

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

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

    = techno-specific “driver” Techno name Icons made by Freepik from www.flaticon.com
  29. 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
  30. 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)
  31. 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)
  32. WHY ? Port & Adapters : entrypoints UseCase RepositoryInterface Entity

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

    /Symfony MemberController MemberSerializer WHY ? Port & Adapters : entrypoints
  34. 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
  35. 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)
  36. 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
  37. WHY ? Command / Query Separation RepositoryInterface Entity Icons made

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

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

    DTO • user intention • primitive values /Application /Command RegisterMember RegisterMemberHandler /Query GetNewMembers GetNewMembersHandler
  40. 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
  41. 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)
  42. WHY ? Command / Query Separation /Application /Command RegisterMember RegisterMemberHandler

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

    only when it simplifies code Ex : custom DTO with less fields
  44. 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
  45. 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
  46. WHY ? Domain Driven Design Make domain logic explicit

  47. 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
  48. 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
  49. 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
  50. WHY ? Before DDD : primitive types only... Icons made

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

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

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

    Design Testing Strategy Gotchas Pro-Tips Icons made by Freepik from www.flaticon.com
  54. 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
  55. 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
  56. Gotchas ? Pro-Tips ? Hexagonal Architecture Command / Query Separation

    Domain Driven Design Testing Strategy Gotchas Pro-Tips Icons made by Freepik from www.flaticon.com
  57. 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
  58. 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 !