Создан  demius PM 4 года назад; Обновил  demius PM 7 месяцев назад

Подробная структура исходного кода проекта.

{{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-объекты содержимого форм (определяют правила валидации для конкретной формы, но могут содержать и более сложную логику связанную с данными в них хранящими)
        • Doc
        • Project
        • Task
        • User
      • 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?