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

Avoiding the mud

Avoiding the mud

It’s easy to get started on an application with Symfony2 and its ecosystem creating bundle and mapping entities to forms and to a database to create a prototype. The problem is growing it beyond this to create a maintainable application. With simple entity mapping exposing the data it contains business logic quickly ends up spread around the application. We can end up with a tangle of business logic and a large tangle of related entities. We may have multiple bundles but they are often dependent on each other. Code which may be useful in other applications becomes tangled up with the application specific code.

This talk looks at some of the ways we can avoid these situations arising. How we can create a solid maintainable application. How we can avoid painting ourselves into a corner making it difficult to make changes. How we can avoid our application becoming a big ball of mud. It will look at carefully identifying what bundles should be used for and more importantly what they shouldn’t be used for. How we can decide what bundles we should have and what goes in each. How we can keep parts of our application cleanly separated from each other and the advantages this can bring.

Richard Miller

November 08, 2013
Tweet

More Decks by Richard Miller

Other Decks in Technology

Transcript

  1. avoiding
    the mud

    View Slide

  2. richard miller
    @mr_r_miller
    richardmiller.co.uk
    sensiolabs uk

    View Slide

  3. http://www.flickr.com/photos/brostad/8583990904

    View Slide

  4. what makes
    an application
    fragile?

    View Slide

  5. single
    responsibility
    principle

    View Slide

  6. a class or module
    should have one, and
    only one, reason to
    change

    View Slide

  7. dependency
    inversion
    principle

    View Slide

  8. High-level
    modules should
    not depend on
    low-level modules

    View Slide

  9. Both should depend
    on abstractions.

    View Slide

  10. should
    everything go
    in a bundle?

    View Slide

  11. no

    View Slide

  12. no
    (imo)

    View Slide

  13. HumanResourcesBundle

    View Slide

  14. HumanResourcesBundle HumanResources
    responsible for
    framework
    related code
    responsible for
    domain
    related code

    View Slide

  15. HumanResourcesBundle HumanResources

    View Slide

  16. better?

    View Slide

  17. still tightly
    coupled

    View Slide

  18. AbsenceEntity
    Request
    Absence
    Form
    Cancel
    Absence
    Form
    Approve
    Absence
    Form
    Change
    Absence
    Type
    Form

    View Slide

  19. entities
    coupled to
    forms

    View Slide

  20. complex
    validation

    View Slide

  21. form events

    View Slide

  22. entity is
    responsible for
    validating user
    input

    View Slide

  23. AbsenceEntity
    Request
    Absence
    Form
    Cancel
    Absence
    Form
    Approve
    Absence
    Form
    Change
    Absence
    Type
    Form
    Request
    Absence
    Command
    Cancel
    Absence
    Command
    Approve
    Absence
    Command
    Change
    Absence
    Type
    Command

    View Slide

  24. simpler validation

    View Slide

  25. command name
    captures the
    domain action

    View Slide

  26. but more
    classes!

    View Slide

  27. simpler
    classes

    View Slide

  28. repetition?

    View Slide

  29. technical complexity
    reduced

    View Slide

  30. what else?

    View Slide

  31. entities
    coupled to
    templates

    View Slide

  32. AbsenceEntity
    upcoming
    .html
    .twig
    details
    .html
    .twig
    calendar
    .html
    .twig
    unapproved
    .html
    .twig

    View Slide

  33. logic in
    templates

    View Slide

  34. {% if absence.endDate < date() %}
    past
    {% else %}
    absence.status
    {% endif %}
    getDisplayStatus()
    AbsenceEntity
    getEndDate()
    getStatus()

    View Slide

  35. if in php
    method it
    could be tested

    View Slide

  36. so let's move it
    to the entity

    View Slide

  37. getDisplayStatus()
    AbsenceEntity
    getEndDate()
    getStatus()
    getDisplayStatus()
    {{ absence.displayStatus }}

    View Slide

  38. but presentational
    logic is not a domain
    responsibility

    View Slide

  39. AbsenceEntity
    Request
    Absence
    Form
    Cancel
    Absence
    Form
    Approve
    Absence
    Form
    Change
    Absence
    Type
    Form
    Absence
    View
    upcoming
    .html
    .twig
    details
    .html
    .twig
    calendar
    .html
    .twig
    unapproved
    .html
    .twig
    getStatus()

    View Slide

  40. commands are now
    responsible for
    capturing data for an
    action

    View Slide

  41. views are responsible for
    providing data for
    presentation

    View Slide

  42. so what is our
    entity
    responsible
    for?

    View Slide

  43. Request
    Absence
    Controller
    Absence
    Entity
    setReason($reason)
    setStatus(AbsenceEntity::CANCELLED)
    setCancellationDate(new DateTime())
    Cancel
    Absence
    Command
    getReason()

    View Slide

  44. the controller is responsible
    for state in the model

    View Slide

  45. Request
    Absence
    Controller
    Absence
    Entity
    cancel($reason)
    Cancel
    Absence
    Command
    getReason()

    View Slide

  46. the entity now
    protects its state

    View Slide

  47. it exposes
    behaviours instead

    View Slide

  48. Cancel
    Absence
    Command
    Absence
    Entity
    Validate internal consistency
    Validate user input

    View Slide

  49. Request
    Absence
    Controller
    Absence
    Entity
    cancel($reason)
    Cancel
    Absence
    Command
    getReason()

    View Slide

  50. AbsenceEntity
    cancel($reason)
    Cancel
    Absence
    Command
    Handler
    Request
    Absence
    Controller
    Absence
    Entity
    cancel($reason)
    Cancel
    Absence
    Command

    View Slide

  51. infrastructure?

    View Slide

  52. persistence is not a domain
    responsibility

    View Slide

  53. but the repository is
    part of the domain

    View Slide

  54. Absences
    (Interface)
    findById($id)
    findUpcomingByMember($member)
    findAllByMember($member)
    Doctrine
    ORM
    Absence
    Repository
    Doctrine
    ODM
    Absence
    Repository
    Guzzle
    Client
    Absence
    Repository
    Infrastructure
    Domain

    View Slide

  55. AbsenceEntity
    Absences
    Domain Bundle
    Service
    Config
    Infrastructure
    Doctrine
    ORM
    Absence
    Repository
    Mapping
    Config

    View Slide

  56. Absences
    findById($id)
    findUpcomingByMember($member)
    findAllByMember($member)
    add(AbsenceEntity $absence)

    View Slide

  57. how often is persistence
    changed?

    View Slide

  58. doctrine events

    View Slide

  59. OnUpdate
    Event
    AbsenceEntity Dispatches

    View Slide

  60. doctrine domain events

    View Slide

  61. OnUpdate
    Event
    AbsenceEntity Dispatches

    View Slide

  62. Absence
    Cancelled
    Event
    Cancel
    Absence
    Command
    Handler
    Dispatches

    View Slide

  63. what bundles
    should I have?

    View Slide

  64. how big should
    they be?

    View Slide

  65. maybe just
    one

    View Slide

  66. don't split if they
    will be coupled
    anyway

    View Slide

  67. so could be quite big

    View Slide

  68. split out code
    reusable in
    other
    applications

    View Slide

  69. HumanResources
    CustomLoggingBundle
    HumanResourcesBundle

    View Slide

  70. HumanResourcesBundle HumanResources
    CustomLoggingBundle CustomLogging
    CustomLoggingBundle
    CustomLoggingBundle CustomLogging
    HumanResourcesBundle

    View Slide

  71. HumanResources
    CustomLoggingBundle CustomLogging
    CustomLoggingBundle
    CustomLoggingBundle CustomLogging
    HumanResourcesBundle

    View Slide

  72. so could be quite small

    View Slide

  73. split application
    bundles when
    domain is big
    enough to split

    View Slide

  74. and can be decoupled

    View Slide

  75. AbsencePlannerBundle AbsencePlanner
    CustomLoggingBundle CustomLogging
    CustomLoggingBundle
    RecruitmentBundle Recruitment

    View Slide

  76. Message Queue/REST
    AbsencePlannerBundle AbsencePlanner
    CustomLoggingBundle CustomLogging
    CustomLoggingBundle
    RecruitmentBundle Recruitment

    View Slide

  77. anything else to split out?

    View Slide

  78. AbsencePlannerBundle AbsencePlanner
    RecruitmentBundle Recruitment
    AbsencePlannerBundle AbsencePlanner
    CustomLoggingBundle CustomLogging
    CustomLoggingBundle
    RecruitmentBundle Recruitment
    User Management

    View Slide

  79. !=
    Staff
    Member
    Entity
    User
    Entity

    View Slide

  80. AbsencePlannerBundle AbsencePlanner
    AbsencePlannerBundle AbsencePlanner
    CustomLoggingBundle CustomLogging
    CustomLoggingBundle
    RecruitmentBundle Recruitment
    UserManagementBundle

    View Slide

  81. so bundles could vary
    hugely in size

    View Slide

  82. being decoupled is the
    important thing

    View Slide

  83. http://www.flickr.com/photos/burge5000/22568539/

    View Slide

  84. http://williamdurand.fr/
    http://verraes.net/#blog
    http://www.whitewashing.de/

    View Slide

  85. questions?
    thank you

    View Slide