Отделение бизнес логики от фреймворка Symfony?

Привет всем. Только открываю для себя "правильное" проектирование архитектуры ПО, и начал писать пет проект за основу которого выбрал чистую архитектуру. Дядя Боб пишет, что фреймворк это деталь и его идея понятна, но когда на практике я начал отделять бизнес слой от слоя инфры немного впал в ступор. То есть например в контексте разработки на симфони, как можно отделить сущности и репозитории от самой симфони? Мы же не имеет больше права генерировать их. Как поступать в этом случае? Писать свои реализации? Проясните пожалуйста данную тему.
  • Вопрос задан
  • 575 просмотров
Решения вопроса 1
Maksclub
@Maksclub Куратор тега PHP
maksfedorov.ru
Генератор — просто инструмент для помощи, по итогу сущности чисты, не считая аннотаций/атрибутов для маппинга в ORM, но это просто мета-информации и завязка не существенна (не считая маленького компромисса с ArrayCollection). То есть если вы выберите др ORM, то эти аннотации вам не помешают никак, просто лишние заюзанные классы аннотаций

Имея сущности доктрины — у нас не связанный от фреймворка код, пишите спокойно бизнесуху, не обращая внимания на то, как оно потом маппится. То есть практически все по каннонам

Чтобы отделить репозиторий от домена — просто в домене делайте интерфейс, а вот реализация этого репозитория будет в Infrastrucure Layer, но это избыточно... риск минимальный, если сделаете не совсем по канону, а именно риск стоит как основной аргумент такового отделения (не просто же вы словам следуете, а причину понимаете?)
Разработка строится на компромисах, если смените доктрину на др ORM — так и так писать новые репозиторий, вероятность низкая и многие например не делают такие интерфейсы — слишком усложнит код...
Вам надо будет просто репозиторий в маппинге ORM\Repository заменить в таком случае

Некоторые компании пишут интерфейсы и сильно усложняют код, но тк риск минимальный изменений, то такое усложнение приводит как правило к усложнению и не более. Следуйте здравому смыслу.
Мой довод нельзя раскручивать "ну раз минимальный, то завяжусь по полной". Все же отделение логики надо делать

чтобы все же соблюсти эту чистоту
и не сильно усложнить, то можно систему разбить на модули/фичи
и каждый модуль будет иметь такие слои
почему лучше это сделать:
- реализация репозиториев хоть и инфраструктурная, но правится часто при изменении домена, а значит сильно далеко прятать не вариант, иначе сильно все будет размазано... потому модуль делают трехслойным (инфра, домен и апликейшн слой)
пример

617588c41bdde421641847.png
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
@zhainar
Гуглю за вас
условно вам не нужно больше писать в коде свой query builder, вы делегируете это интерфейсу репозитория. У каждой сущности свой интерфейс репозитория, не нужно писать общий интерфейс для всех сущностей.

Например, если в коде у вас было:
$companyEmployees = $entityManager
    ->createQueryBuilder()
    ->where('companyId = :company')
    ->where('active = true')
    ->fetchCollection();


то этот код заменяется на интерфейс

interface CompanyRepository {
    public function getActiveEmployees(): array;
}


а в коде вызывается метод этого интерфейса, при этом реализация уже может быть любой

class DoctrineCompanyRepository implements CompanyRepository {
    public function getActiveEmployees()
    {
         return $entityManager
             ->createQueryBuilder()
             ->where('companyId = :company')
             ->where('active = true')
             ->fetchCollection();
    }
}

class EloquentCompanyRepository implements CompanyRepository {
    public function getActiveEmployees()
    {
         return Company::where()->where()->get();
    }
}

class MockCompanyRepository implements CompanyRepository {
    public function getActiveEmployees()
    {
         return [new Employee($name, $position), new Employee($name2, $position2)];
    }
}

class SqlCompanyRepository implements CompanyRepository {
    public function getActiveEmployees()
    {
         return Db::query('select * from company_employees t where t.company_id = :id and ...')->fetchAll();
    }
}


соответственно в вашем коде вы должны передать объект нужного репозитория
class CompanyController 
{
    public function fireAllAction()
    {
         $companyService = new CompanyService(new DoctrineCompanyRepository());
         ...
    }
}


class CompanyServiceTest
{
    public function testFireAllMethod()
    {
         $companyService = new CompanyService(new MockCompanyRepository());
    }
}
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы