title: Doctrine Entities and business logic in a Symfony application
body: <p><strong>Any ideas / feedback are welcome :)</strong> </p>
  
  <p>I run into a problem in how to <strong>handle business logic</strong> around my <strong>Doctrine2 entities</strong> in a big <strong>Symfony2 application</strong>. (Sorry for the post length)</p>
  
  <p>After reading many blogs, cookbook and others ressources, I find that :</p>
  
  <ul>
  <li>Entities might be used only for data mapping persistence ("anemic model"),</li>
  <li>Controllers must be the more slim possible,</li>
  <li>Domain models must be decoupled from persistence layer (entity do not know entity manager)</li>
  </ul>
  
  <p>Ok, I'm totally agree with it, but :
  <strong>where and how handle complex bussiness rules on domain models ?</strong></p>
  
  <hr>
  
  <h2>A simple example</h2>
  
  <p><strong>OUR DOMAIN MODELS :</strong></p>
  
  <blockquote>
    <ul>
    <li>a <strong>Group</strong> can use <strong>Roles</strong></li>
    <li>a <strong>Role</strong> can be used by different <strong>Groups</strong></li>
    <li>a <strong>User</strong> can belong to many <strong>Groups</strong> with many <strong>Roles</strong>,</li>
    </ul>
  </blockquote>
  
  <p>In a <strong>SQL</strong> persistence layer, we could modelize these relations as :</p>
  
  <p><img src="https://i.stack.imgur.com/yhs3t.png" alt="enter image description here"></p>
  
  <p><strong>OUR SPECIFIC BUSINESS RULES :</strong></p>
  
  <blockquote>
    <ul>
    <li><strong>User</strong> can have <strong>Roles</strong> in <strong>Groups</strong> <em>only if Roles is attached to the Group</em>.</li>
    <li>If we detach a <strong>Role R1</strong> from a <strong>Group G1</strong>, all <em>UserRoleAffectation with the Group G1 and Role R1 must be deleted</em></li>
    </ul>
  </blockquote>
  
  <p><strong>This is a very simple example, but i'd like to kown the best way(s) to manage these business rules.</strong></p>
  
  <hr>
  
  <h2>Solutions found</h2>
  
  <p><strong>1- Implementation in Service Layer</strong></p>
  
  <p>Use a specific Service class as  :</p>
  
  <pre><code>class GroupRoleAffectionService {
  
    function linkRoleToGroup ($role, $group)
    { 
      //... 
    }
  
    function unlinkRoleToGroup ($role, $group)
    {
      //business logic to find all invalid UserRoleAffectation with these role and group
      ...
  
      // BL to remove all found UserRoleAffectation OR to throw exception.
      ...
  
      // detach role  
      $group-&gt;removeRole($role)
  
      //save all handled entities;
      $em-&gt;flush();   
  }
  </code></pre>
  
  <ul>
  <li>(+) one service per class / per business rule</li>
  <li>(-) API entities is not representating to domain : it's possible to call <code>$group-&gt;removeRole($role)</code> out from this service.</li>
  <li>(-) Too many service classes in a big application ?</li>
  </ul>
  
  <hr>
  
  <p><strong>2 - Implementation in Domain entity Managers</strong></p>
  
  <p>Encapsulate these Business Logic in specific "domain entities manager", also call Model Providers :</p>
  
  <pre><code>class GroupManager {
  
      function create($name){...}
  
      function remove($group) {...}
  
      function store($group){...}
  
      // ...
  
      function linkRole($group, $role) {...}
  
      function unlinkRoleToGroup ($group, $role)
      {
  
      // ... (as in previous service code)
      }
  
      function otherBusinessRule($params) {...}
  }
  </code></pre>
  
  <ul>
  <li>(+) all businness rules are centralized</li>
  <li>(-) API entities is not representating to domain : it's possible to call $group->removeRole($role) out from service...</li>
  <li>(-) Domain Managers becomes FAT managers ?</li>
  </ul>
  
  <hr>
  
  <p><strong>3 - Use Listeners when possible</strong></p>
  
  <p>Use symfony and/or Doctrine event listeners :</p>
  
  <pre><code>class CheckUserRoleAffectationEventSubscriber implements EventSubscriber
  {
      // listen when a M2M relation between Group and Role is removed
      public function getSubscribedEvents()
      {
          return array(
              'preRemove'
          );
      }
  
     public function preRemove(LifecycleEventArgs $event)
     {
      // BL here ...
     }
  </code></pre>
  
  <hr>
  
  <p><strong>4 - Implement Rich Models by extending entities</strong></p>
  
  <p>Use Entities as sub/parent class of Domain Models classes, which encapsulate lot of Domain logic. But this solutions seems more confused for me.</p>
  
  <hr>
  
  <p><strong>For you, what is the best way(s) to manage this business logic, focusing on the more clean, decoupled, testable code ?</strong> Your feedback and good practices ? Have you concrete examples ?</p>
  
  <p><em>Main Ressources :</em></p>
  
  <ul>
  <li><a href="https://stackoverflow.com/questions/14400120/symfony-managing-entities">Symfony managing entities</a></li>
  <li><a href="https://stackoverflow.com/questions/8021730/symfony2-doctrine-having-to-put-business-logic-in-my-controller-and-duplicatin">Symfony2/Doctrine, having to put business logic in my controller? And duplicating controller?</a></li>
  <li><a href="https://stackoverflow.com/questions/12179812/extending-doctrine-entity-in-order-to-add-business-logic">Extending Doctrine Entity in order to add business logic</a></li>
  <li><a href="http://iamproblematic.com/2012/03/12/putting-your-symfony2-controllers-on-a-diet-part-2/" rel="noreferrer">http://iamproblematic.com/2012/03/12/putting-your-symfony2-controllers-on-a-diet-part-2/</a></li>
  <li><a href="http://l3l0.eu/lang/en/2012/04/anemic-domain-model-problem-in-symfony2/" rel="noreferrer">http://l3l0.eu/lang/en/2012/04/anemic-domain-model-problem-in-symfony2/</a></li>
  <li><a href="https://leanpub.com/a-year-with-symfony" rel="noreferrer">https://leanpub.com/a-year-with-symfony</a></li>
  </ul>