Save 37% off PRO during our Black Friday Sale! »

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.

Ab44158da0498db70754ee8061e69c31?s=128

Richard Miller

November 08, 2013
Tweet

Transcript

  1. avoiding the mud

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

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

  4. what makes an application fragile?

  5. single responsibility principle

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

    reason to change
  7. dependency inversion principle

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

  9. Both should depend on abstractions.

  10. should everything go in a bundle?

  11. no

  12. no (imo)

  13. HumanResourcesBundle

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

    related code
  15. HumanResourcesBundle HumanResources

  16. better?

  17. still tightly coupled

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

    Change Absence Type Form
  19. entities coupled to forms

  20. complex validation

  21. form events

  22. entity is responsible for validating user input

  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
  24. simpler validation

  25. command name captures the domain action

  26. but more classes!

  27. simpler classes

  28. repetition?

  29. technical complexity reduced

  30. what else?

  31. entities coupled to templates

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

    unapproved .html .twig
  33. logic in templates

  34. {% if absence.endDate < date() %} past {% else %}

    absence.status {% endif %} getDisplayStatus() AbsenceEntity getEndDate() getStatus()
  35. if in php method it could be tested

  36. so let's move it to the entity

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

  38. but presentational logic is not a domain responsibility

  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()
  40. commands are now responsible for capturing data for an action

  41. views are responsible for providing data for presentation

  42. so what is our entity responsible for?

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

    Absence Command getReason()
  44. the controller is responsible for state in the model

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

  46. the entity now protects its state

  47. it exposes behaviours instead

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

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

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

    Entity cancel($reason) Cancel Absence Command
  51. infrastructure?

  52. persistence is not a domain responsibility

  53. but the repository is part of the domain

  54. Absences (Interface) findById($id) findUpcomingByMember($member) findAllByMember($member) Doctrine ORM Absence Repository Doctrine

    ODM Absence Repository Guzzle Client Absence Repository Infrastructure Domain
  55. AbsenceEntity Absences Domain Bundle Service Config Infrastructure Doctrine ORM Absence

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

  57. how often is persistence changed?

  58. doctrine events

  59. OnUpdate Event AbsenceEntity Dispatches

  60. doctrine domain events

  61. OnUpdate Event AbsenceEntity Dispatches

  62. Absence Cancelled Event Cancel Absence Command Handler Dispatches

  63. what bundles should I have?

  64. how big should they be?

  65. maybe just one

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

  67. so could be quite big

  68. split out code reusable in other applications

  69. HumanResources CustomLoggingBundle HumanResourcesBundle

  70. HumanResourcesBundle HumanResources CustomLoggingBundle CustomLogging CustomLoggingBundle CustomLoggingBundle CustomLogging HumanResourcesBundle

  71. HumanResources CustomLoggingBundle CustomLogging CustomLoggingBundle CustomLoggingBundle CustomLogging HumanResourcesBundle

  72. so could be quite small

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

  74. and can be decoupled

  75. AbsencePlannerBundle AbsencePlanner CustomLoggingBundle CustomLogging CustomLoggingBundle RecruitmentBundle Recruitment

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

  77. anything else to split out?

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

    Recruitment User Management
  79. != Staff Member Entity User Entity

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

  81. so bundles could vary hugely in size

  82. being decoupled is the important thing

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

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

  85. questions? thank you