Мне нужна помощь с чем-то, что я не могу понять, вместилище а также Услуги / Использование регистра шаблон (часть DDD дизайн) Я хочу реализовать в моем следующем (Laravel PHP) проекте.
Кажется, все ясно. Только одна часть DDD, которая сбивает с толку, — это структуры данных из репозиториев. Люди, кажется, выбирают структуры данных, которые должен возвращать репозиторий (массивы или сущности), но все это имеет недостатки. Одним из них является представление о моем опыте в прошлом. И один из них — это то, что у вас нет интерфейсов для простых структур данных (массив или атрибуты простого объекта).
Начну с объяснения опыта предыдущего проекта. У этого проекта были недостатки, но есть некоторые сильные стороны, из которых я научился и хотел бы видеть в своем новом проекте, но с решением некоторых ошибок проектирования.
Предыдущий опыт
В прошлом я создавал веб-сайт, который был ориентирован на API с использованием инфраструктуры Kohana и Doctrine 2 ORM (шаблон отображения данных). Поток выглядел так:
Контроллер веб-сайта → API-клиент (вызовы HMVC) → API-контроллер → Пользовательский репозиторий → Doctrine 2 ORM Собственный репозиторий / Entity-manager
Мой пользовательский репозиторий возвратил простые массивы с помощью Doctrine2 DQL. Doctrine2 рекомендует данные результата массива для операций только для чтения. И да, это сделало мой сайт красивым и легким. Контроллер API только что преобразовал данные массива в JSON. Просто как тот.
В прошлом моя компания создавала проекты, полностью полагаясь на загруженные объекты Doctrine2, и об этом мы сожалели из-за производительности.
Мой REST API поддерживает запросы, такие как
/api/users?include_latest_adverts=2&include_location=true
на ресурсе пользователей. API контроллер пройден include_location
в хранилище, которое непосредственно включает в себя отношение местоположения. Контроллер прочитал latest_adverts=2
и вызвал хранилище рекламы, чтобы получить последние 2 рекламы каждого пользователя. Массивы были возвращены.
Например, первый пользовательский массив:
[
name
avatar
adverts [
advert 1 [
name
price
]
advert 2 [
….
]
]
]
Это оказалось очень успешным. Весь мой сайт использовал API. Было бы очень легко добавить нового клиента, потому что API отлично работал, уже используя oauth. Весь сайт работает на нем.
Но у этого дизайна тоже были недостатки. Мой контроллер все еще содержал много логики для проверки, рассылки, параметров или фильтров, таких как has_adverts=true
получить пользователей только с рекламой. Это означало бы, что, если бы я создал новый порт, такой как полностью новый интерфейс CLI, мне пришлось бы дублировать множество этих контроллеров из-за всей проверки и т. Д. Но не было бы дублирования, если бы я создал нового клиента. Так что хотя бы одна проблема была решена 🙂
Мои админ-панели были полностью связаны с хранилищем doctrine2 / сущностью-менеджером для ускорения разработки (вроде). Зачем? Потому что в моем API были жирные контроллеры со специальными функциями только для веб-сайта (специальная проверка, рассылка для регистрации и т. Д.). Я должен был бы переделать работу или сделать рефакторинг. Поэтому решили использовать сущности напрямую, чтобы все еще иметь какой-то четкий способ написания кода вместо переписывания всех моих контроллеров API и перемещения их в службы (для сайта & админ) например. Время было проблемой в исправлении моих ошибок в дизайне.
Для моего следующего проекта я хочу, чтобы весь код проходил через мои собственные репозитории и сервисы. Один поток для хорошего разделения.
Новый проект (с использованием идей DDD) и дилемма со структурами данных
Хотя мне нравится идея быть ориентированной на API, я не хочу, чтобы мой следующий проект был ориентирован на API в основном, потому что я думаю, что те же функциональные возможности должны быть доступны без промежуточного протокола HTTP. Я хочу спроектировать ядро, используя идеи DDD.
Но мне понравилась идея использовать слой, который просто говорит как API и возвращает простые массивы. Идеальная база для любого нового порта, включая мой собственный интерфейс. Моя идея состоит в том, чтобы рассматривать мои классы служб как интерфейс API (возвращать данные массива), выполнять проверку и т. Д. Я мог бы иметь службы специально для веб-сайта (регистрации) и простые службы, используемые администратором или фоновыми процессами. В некоторых случаях администрирования в любом случае для простого редактирования CRUD Сервис не требовался, я мог просто использовать репозитории напрямую. Контроллеры были бы очень тонкими. При этом создание реального REST API было бы просто вопросом создания новых контроллеров, использующих те же сервисы, что и мои классы контроллеров внешнего интерфейса.
Для внутренней логики, такой как бизнес-правила, было бы полезно иметь Entities (чистые интерфейсы) вместо массивов из репозиториев. Таким образом, я мог бы выиграть от определения некоторых методов, которые делали логику на основе атрибутов. НО, если бы я использовал Doctrine2, а мои репозитории всегда возвращали сущности, мое приложение сильно пострадает от производительности !!
Одна структура данных обеспечивает производительность, но не имеет четких интерфейсов, другая обеспечивает четкие интерфейсы, но плохую производительность при использовании шаблона Data Pattern, такого как Doctrine 2 (сейчас или в будущем). Также я мог бы получить два типа данных, которые могли бы сбить с толку.
Я думал о чем-то похожем на этот поток:
Контроллер (тонкий) → UserService (включая проверку) → UserRepository (просто хранилище) → Eloquent ORM
Почему Eloquent вместо Doctrine2? Потому что я хочу немного придерживаться того, что является общим в рамках Laravel и сообщества. Таким образом, я мог бы извлечь выгоду из сторонних модулей, например, для создания интерфейсов администратора или аналогичных на основе моделей (в обход моих правил DDD). Помимо использования сторонних модулей, я разработал бы свои основные компоненты, чтобы переключение всегда было простым и не влияло на выбор структуры данных или производительность.
Eloquent — это шаблон активной записи. Таким образом, у меня возникнет соблазн преобразовать эти данные в POPO, как в сущностях Doctrine2. Но нет … как уже было сказано выше, с doctrine2 реальные модели сделали бы систему очень толстой. Поэтому я снова возвращаюсь к простым массивам. Знание этого будет работать как для любой другой реализации в будущем.
Но это плохо всегда полагаться на массивы. Особенно при создании внутренних бизнес-правил. Разработчик должен был бы угадать значения для массивов, не иметь автодополнения в своей IDE, не мог бы иметь специальные методы, как в классах Entity. Но делать два способа работы с данными тоже плохо. Или я просто перфекционист;) Я хочу ОДНУ ясную структуру данных для всех!
Создание интерфейсов и POPO означало бы много дублирующихся работ. Мне нужно будет преобразовать модель Eloquent (просто преобразователь таблиц, а не сущность) в объект сущности, реализующий этот интерфейс. Все это дополнительная работа. И, в конце концов, мой последний уровень будет похож на API, поэтому снова преобразую его в массивы. Это тоже дополнительная работа. Массивы кажутся снова заключенными.
Казалось, что так легко читать в DDD и Hexagonal. Вроде так логично! Но на самом деле я борюсь с этой простой проблемой, пытаясь придерживаться принципов ООП. Я хочу использовать массивы, потому что это единственный способ быть на 100% уверенным, что я не зависим от выбора модели и запроса от моего ORM относительно производительности и т. Д., И у меня нет дубликатов при преобразовании в массивы для представлений или API. Но нет четкого договора о том, как может выглядеть пользовательский массив. Я хочу ускорить мой проект, используя эти шаблоны, а не замедлять их 🙂 Так что не вариант иметь много конвертеров.
Сейчас я читаю много тем. Один делает ПОПО & интерфейсы, которые соответствуют соответствующим сущностям, таким как Doctrine2, могут вернуться, но со всей дополнительной работой для Eloquent. Переключение на Doctrine2 должно быть достаточно простым, но оно может так сильно повлиять на производительность, иначе потребуется преобразовать данные массива Doctrine2 в эти собственные интерфейсы сущностей. Другие предпочитают возвращать простые массивы.
Один убеждает людей использовать Doctrine2 вместо Eloquent, но они не учитывают тот факт, что Doctrine2 тяжелый и вам действительно нужно использовать результаты массива для операций только для чтения.
Мы проектируем репозитории, чтобы быть изменяемыми, верно? Не потому, что это «красиво» только по дизайну. Итак, как мы можем полагаться на полные сущности, если они так сильно влияют на производительность или дублирующую работу? Даже при использовании только Doctrine2 (в сочетании) эта же проблема может возникнуть из-за его производительности!
Все реализации ORM могли бы возвращать массивы, таким образом, никакой дублирующей работы там нет. Хорошая производительность. Но мы пропускаем четкие контракты. И у нас нет интерфейсов для массивов или атрибутов классов (в качестве обходного пути) … Тьфу;)
Я просто пропускаю отсутствующий блок в наших языках программирования? Интерфейсы на простых структурах данных
Разумно ли создавать все массивы и использовать расширенную бизнес-логику для взаимодействия с этими массивами? Таким образом, нет классов с понятными интерфейсами. Любые предварительно вычисленные данные (обычно возвращаемые методом Entity) будут находиться в ключе массива, определенного классом Service. если не разумно, какова альтернатива, учитывая все вышеперечисленное?
Я был бы очень признателен, если бы кто-то с большим опытом в этой «области», учитывая производительность, различные реализации ORM и т. Д., Мог бы рассказать мне, как он / она справился с этим?
Заранее спасибо!
Я думаю, что вы имеете дело с чем-то похожим, с чем я борюсь. Решение, которое я думаю, работает лучше всего:
Других решений пока нет …