duancoubeng5909 2013-10-03 08:48
浏览 64
已采纳

Symfony应用程序中的Doctrine实体和业务逻辑

Any ideas / feedback are welcome :)

I run into a problem in how to handle business logic around my Doctrine2 entities in a big Symfony2 application. (Sorry for the post length)

After reading many blogs, cookbook and others ressources, I find that :

  • Entities might be used only for data mapping persistence ("anemic model"),
  • Controllers must be the more slim possible,
  • Domain models must be decoupled from persistence layer (entity do not know entity manager)

Ok, I'm totally agree with it, but : where and how handle complex bussiness rules on domain models ?


A simple example

OUR DOMAIN MODELS :

  • a Group can use Roles
  • a Role can be used by different Groups
  • a User can belong to many Groups with many Roles,

In a SQL persistence layer, we could modelize these relations as :

enter image description here

OUR SPECIFIC BUSINESS RULES :

  • User can have Roles in Groups only if Roles is attached to the Group.
  • If we detach a Role R1 from a Group G1, all UserRoleAffectation with the Group G1 and Role R1 must be deleted

This is a very simple example, but i'd like to kown the best way(s) to manage these business rules.


Solutions found

1- Implementation in Service Layer

Use a specific Service class as :

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->removeRole($role)

    //save all handled entities;
    $em->flush();   
}
  • (+) one service per class / per business rule
  • (-) API entities is not representating to domain : it's possible to call $group->removeRole($role) out from this service.
  • (-) Too many service classes in a big application ?

2 - Implementation in Domain entity Managers

Encapsulate these Business Logic in specific "domain entities manager", also call Model Providers :

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) {...}
}
  • (+) all businness rules are centralized
  • (-) API entities is not representating to domain : it's possible to call $group->removeRole($role) out from service...
  • (-) Domain Managers becomes FAT managers ?

3 - Use Listeners when possible

Use symfony and/or Doctrine event listeners :

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 ...
   }

4 - Implement Rich Models by extending entities

Use Entities as sub/parent class of Domain Models classes, which encapsulate lot of Domain logic. But this solutions seems more confused for me.


For you, what is the best way(s) to manage this business logic, focusing on the more clean, decoupled, testable code ? Your feedback and good practices ? Have you concrete examples ?

Main Ressources :

  • 写回答

5条回答 默认 最新

  • duangangpin078794 2013-10-08 03:52
    关注

    I find solution 1) as the easiest one to maintain from longer perspective. Solution 2 leads bloated "Manager" class which will eventually be broken down into smaller chunks.

    http://c2.com/cgi/wiki?DontNameClassesObjectManagerHandlerOrData

    "Too many service classes in a big application" is not a reason to avoid SRP.

    In terms of Domain Language, I find the following code similar:

    $groupRoleService->removeRoleFromGroup($role, $group);
    

    and

    $group->removeRole($role);
    

    Also from what you described, removing/adding role from group requires many dependencies (dependency inversion principle) and that could be hard with a FAT/bloated manager.

    Solution 3) looks very similar to 1) - each subscriber is actually service automatically triggered in background by Entity Manager and in simpler scenarios it can work, but troubles will arise as soon the action (adding/removing role) will require a lot of context eg. which user performed the action, from which page or any other type of complex validation.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(4条)

报告相同问题?

悬赏问题

  • ¥15 关于#matlab#的问题:期望的系统闭环传递函数为G(s)=wn^2/s^2+2¢wn+wn^2阻尼系数¢=0.707,使系统具有较小的超调量
  • ¥15 FLUENT如何实现在堆积颗粒的上表面加载高斯热源
  • ¥30 截图中的mathematics程序转换成matlab
  • ¥15 动力学代码报错,维度不匹配
  • ¥15 Power query添加列问题
  • ¥50 Kubernetes&Fission&Eleasticsearch
  • ¥15 報錯:Person is not mapped,如何解決?
  • ¥15 c++头文件不能识别CDialog
  • ¥15 Excel发现不可读取的内容
  • ¥15 关于#stm32#的问题:CANOpen的PDO同步传输问题