Любые идеи/отзывы приветствуются:)
У меня возникла проблема с обработкой бизнес-логики вокруг моих объектов Doctrine2 в большом приложении Symfony2. (Извините за длину сообщения)
После чтения многих блогов, поваренной книги и других ресурсов, я обнаружил, что:
- Сущности могут использоваться только для сохранения данных ( "Анемическая модель" ),
- Контроллеры должны быть более стройными,
- Модели домена должны быть отделены от уровня персистентности (сущность не знает диспетчера сущностей)
Хорошо, я полностью согласен с этим, но: где и как обрабатывать сложные бизнес-правила на моделях домена?
Простой пример
НАШИ ДОМЕННЫЕ МОДЕЛИ:
- a Группа может использовать Роли
- a Роль может использоваться различными группами
- Пользователь может принадлежать многим группам со многими Ролями,
На уровне сохранения SQL мы могли бы моделировать эти отношения как:
НАШИ КОНКРЕТНЫЕ ПРАВИЛА БИЗНЕСА:
- Пользователь может иметь Роли в Группы, только если к группе добавлены роли.
- Если мы отделим Роль R1 от Группы G1, все UserRoleAffectation с Группой G1 и Роль R1 должны быть удалены.
Это очень простой пример, но я хотел бы наилучшим образом описать эти бизнес-правила.
найденные решения
1- Реализация на уровне обслуживания
Используйте определенный класс службы как:
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();
}
- (+) одна услуга за класс/бизнес-правило
- (-) Объекты API не представляют домен: можно вызвать
$group->removeRole($role)
из этой службы. - (-) Слишком много классов обслуживания в большом приложении?
2 - Реализация в Менеджерах сущностей домена
Инкапсулируйте эту бизнес-логику в конкретный "администратор сущностей домена", также вызывайте модель поставщиков:
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) {...}
}
- (+) все правила бизнес-централизованы
- (-) Объекты API не представляют домен: можно вызвать $group- > removeRole ($ role) из службы...
- (-) Менеджеры домена становятся менеджерами FAT?
3 - Использовать прослушиватели, если возможно
Используйте прослушиватели событий symfony и/или Doctrine:
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 - Внедрение Rich Models путем расширения объектов
Использовать Entities как класс sub/parent классов Domain Models, которые инкапсулируют много логики домена. Но для меня это кажется более смущенным.
Для вас наилучшим способом управлять этой бизнес-логикой, фокусируясь на более чистом, развязаемом, проверяемом коде? Ваши отзывы и передовые методы? У вас есть конкретные примеры?
Основные ресурсы:
- Объекты управления Symfony
- Symfony2/Doctrine, нужно поставить бизнес-логику в контроллер? И дублирующий контроллер?
- Расширение Entity Doctrine для добавления бизнес-логики
- http://iamproblematic.com/2012/03/12/putting-your-symfony2-controllers-on-a-diet-part-2/
- http://l3l0.eu/lang/en/2012/04/anemic-domain-model-problem-in-symfony2/
- https://leanpub.com/a-year-with-symfony