Подробная структура исходного кода проекта.
{{to(readMe.md, ‘%title%’)}} - пример wiki-функции, которые я когда-нибудь реализую, не выводятся в документе, но позволяют совершать над документацией разные действия, например этот будет отлавливать изменения в документе и при релизе править файл readMe.md в проекте
Соглашения о расположении
Разделение на слои:
- Точно и четко делим на слои MVC
- Сохраняем максимально легковесные контроллеры, даже если для этого нужны классы с одной функцией
- Из twig-функций вытаскиваем всю логику, кроме элементарной логике представления
- Сервисы подсистем состоящие из одного-двух классов храним в src/Services. Когда появляется четкое понимание, что у подсистемы будем больше классов, выделяем в новую директорию в srv/Service/Subsystem/.
Если подсистема продолжает разрастаться и по планам будет хранить значимую часть функционала проекта, а так же будет иметь
собственные поддиректории, выделяем в srv/Subsystem, (Когда таких станет много, возможно часть из них, в том числе предрасположенные нужно будет вновь убрать в поддиректории) *вот это сомнительно, наверное все же уберем Security в Service когда сами сервисы разделим на презентацию, домен и application
- src/Model для хранения всех моделей application. Для моделей презентации отдельный src/ViewModel с предложением дальнейшего разделения.
Разделение классов, выделение абстракций и полиморфизм
- Внутри основополагающий слоев, выделяем новые подслои по требованию. Например если в подсистеме все две функции лежат в слое сервисов, оставляем один Service класс, при увеличении логики, и понимании как переименовать сервис в <подсистема><Что-то делатель> - разделяем на отдельные сервисы (Например одна две функции работы с проектами -> ProjectService, появилось больше несвязанной логики, сервис перешагнул за 100 строк, разделяем на ProjectContext и ProjectRegistry)
- Не выделяем интерфейсы, пока их реализует только один класс, но проектируем такие классы с учетом предположения, что в будущем нам наверняка потребуется интерфейс
- Первые задачи на новую систему и так очень сложны, чтобы проектировать в них окончательные абстракции. Так что там, где слой должен обращаться к конкретной реализации и она там одна, допустимо вызывать напрямую или под условием instanceof. В следующих задачах расширения такие места необходимо превращать в зависимости от абстракций через введение интерфейсов, хендлеров и т.д. Например при реализации самого первого справочника, Dictionary может создавать свои элементы напрямую, через new DictionaryItem. В другой задаче, при реализации справочника требующего расширенного элемента, эта система переделывается на билдер внутри справочника, переопределяемый в потомках (если понадобится еще более сложная логика, только тогда в другой задачи этот билдер можно выносить в отдельный сервис-фабрику элементов справочника)
Структура директорий
- assets - исходные файлы фронтенда
- components - vue-компоненты
- images - изображения идущие в комплекте
- src_images - исходники изображений
- styles - стили
- код фронтенда
- bin - консольные команды
- config - файлы конфигурации
- common - общие файлы конфигурации
- packages - настройки сторонних пакетов
- routes - файлы роутов (не особо нужны в видел кучи файлов, но так позволяют задавать роуты уникальные для разных окружений)
- services - файлы сервисов.
- dev - конфигурация специфичная для локльной разработки
- packages - настройки сторонних пакетов
- routes - файлы роутов
- services - файлы сервисов.
- prod - конфигурация специфичная для продакшена
- test - конфигурация специфичная для тестов
- docker - файлы постройки контейнеров и настроек для них
- help - встроенная документация
- node_modules - генерится для сборки фронтенда, npm-пакеты
- public - доступная в веб директория сточкой входа и скомпилированным фронтендом
- build - построенный webencore фронтенд
- bundles - куски фронтенда построенные старой системой assetics по хорошему не должно быть, все должно собираться единым образом
- src - исходный код бекенда
- Command - команды
- Contract - Контракты (сецйчас все в куче, но надо разделить по категориям)
- Controller - контроллеры
- Entity - Объекты-сущности, хранящиеся в doctrine должны уехать или в Model или в Persistance
- Event - События пока в основном бизнес, но может быть что еще будет
- EventSubscriber - Подписчики и слушатели событий
- Menu - Подписчики строящие различные меню. Идеологически не являются слушателями событий, но AdminLTE диктует получение меню через события. С другой стороны можно разнести их код по зонам ответственности - проект, задача, документ, еще что-то что захочет добавлять элементы в хлебные крошки или sidebar, но это не отменяет факта, что это Builder, а не subscriber
- Exception - Исключения
- Form - Формы прямой кандидат на вынесение в презентацию
- DTO - dto-объекты содержимого форм (определяют правила валидации для конкретной формы, но могут содержать и более сложную логику связанную с данными в них хранящими)
- Type - Объекты Форм и контролов на них
- Comment
- Doc
- Project
- Task
- User
- Migrations - Миграции БД
- Model - бизнес-модели
- Dto
- Enum
- Template - наборы настроек по умолчанию
- Table - настройки фильтров и сортировок тех или иных таблиц
- Repository - Репозитории получения сущностей из doctrine (обсуждаемый вопрос, хранить ли там специализированные репозитории, хранящие объекты вне doctrine (Сервисы-регистры, Сервисы работы с объектами в nosql-хранилище) *вероятно стоит вынести в Persistance
- Security это тоже кандидат в сервисы
- Hierarchy - сервисы занимающиеся построением и быстрой отдачей карты полномочий
- Voter - Избиратели, решающие предоставлять ли запрошенный доступ
- Service
- Badges - Баджи (цветные метки разных сущностей) может показаться, что презентация, но это бизнес логика, а как отображать решает твиг
- Constraints - Валидаторы
- Dictionary - сервисы работы со справочнками их на самом деле сильно облегчили, и будут еще до одного сервиса
- Doctrine - специфические сервисы для доктрины, кажется это кандидат на Infrastructure
- Type - наши кастомные типы данных
- File - подсистема работы с файлами
- Filler - Сервисы заполнители. Знают как правильно создать один объект из другого, или заполнить один объект данными другого. Умеют догружать нехватающую информацию и проверять сложную логику косистентности, выходящую за пределы доступные entity.
- Monolog - сервисы для работы с монологом кажется это кандидат на Infrastructure
- RequestResolver - сервисы обработки реквеста для подгрузки текуущей сущности
- SpecBuilder - билеры спецификаций вобще подумать нужна ли для них отдельная диреткория, или они будут в своих подсистемах, лежащий там явно относится к Table
- Statistics - подсистема сбора статистики
- Table - подсистема строительства сложных больших таблиц данных
- Twig - Расширения шаблонизатора а вот это явно presentation, причем его бы еще прошерстить и уменьшить количество функций, заменив их на что другое, например более гибкие viewModel, widget и т.д.
- Wiki - подсистема wiki ссылок
- Specification - спецификации, как универсальные, так и специфичные для той или иной модели
- ViewModel - модели для презентации
- ViewTransformer - трансформеры для презентаций создаюие viewModel или json для ajax-роутов
- Kernel.php
- templates - шаблоны страниц
- auth
- comment
- doc
- error - страницы ошибок
- file - виджеты для работы с файлами
- home
- project
- table - виджеты для работы с таблицами
- task
- user - страницы касающиеся пользователей видимые обычному пользователю (Список, свой профиль с возможность редактировать, профили других пользователей)
- user_manager - страницы касающиеся администрирования пользователей
- tests - тесты
- Behat - сервисные классы для behat
- Controller - интеграционные, где краулер ходит по роутам поднятого проекта переименовать бы
- DataFixtures - фикстуры
- features - Еще одни интеграционные, но на греклине надо бы оставить только одни, видимо эти
- unit - собственно истинные юнит-тесты. Структура внутри соответствует структуре src
- translations - файлы переводов
- var - изменяемые файлы
- cache
- <env> - сюда собирается симфоневый кеш классов
- log - для окружения хранящего логи в файлах, сюда складываются эти файлы
- storage
- <env> - здесь лежат базы данных и хранилища, если в данном окружении они видны из проекта
- files - загуженные в проект файлы
- mysql - sql база данных
- redis - nosql база данных
- vendor - генерится при сборке бекэнда, composer-пакеты
Предложения и обсуждения.
(великоваты для комментария, а в виде отдельного документа без системы гиперссылок и других пометок потеряются)
I: Делим по типам, аналогично предложенному симфони: Entity все доктриновские сущности, Enum - Все enum’ы, Services - все сервисы (из этого выбивается симфоневый же security)
- Все сложено по типам, если мне нужно какое-то перечисление я сразу понимаю где искать.
- Подсистема размазана по всему проекту, сложно будет сказать какой сервис где используется и можно ли его выдернуть. (у нас так размазаны формы и их дто по параллельным директориям и сущностям, не разбирая это часть crud или чей-то контрол)
** на данный момент следуем этим путем и присматриваемся к гексагональной архитектуре**
II: Делим по подсистемам, - Security - все связанное с полномочиями и авторизацией, Enitity - сущности доктрины, Objects - Jlob-объекты, Dictionary - справочники (сервисы справочников или и их jlob-объекты и их enum’ы) На это деление намекает services.yml, ищущий сервисы по всему src исключая Entity и Миграции.
- Мы избавляемся (или как минимум уменьшаем помойки в виде директории Service, в которой рядом лежат очень не связанные друг с другом сервисы.
- Когда мы хотим охватить взглядом подсистему не надо шастать по всем директориям в поисках где еще она лежит
- Enum, Exception размазаны по всей системе. И если с первым наверное это и правильно, если нам нужен набор констант подсистемы логично искать его в подсистеме, то со вторым хуже, каждая подсистема будет иметь свой набор Exception?