По своему опыту знаю, PHP очень гибкий, быстрый, удобный, его можно использовать практически для чего угодно. Наиболее развита сейчас, конечно, сфера применения в вэбе, бэкенде мобильных приложений. Возможно, Symfony, лучшее, что есть сейчас в своем роде.
Часто незнающие люди говорят, что вот, столько всякого плохого кода написано на PHP, и значит это плохой язык. Но они путают причину и следствие. PHP в свое время набрал огромную популярность, как раз потому, что на нем можно было проще и быстрее всего создавать проекты. Мало кто знает, но у всеми известного Битрикса была так же версия на C#, но она не обрела такой популярности, и код там еще хуже, и поддерживать его еще сложнее. То есть, PHP, как раз, помог в этом случае всем.
Сначала я читал Эванса. Читаю, что-то воды много, думаю, ну может это такое введение долгое. Читаю дальше, то же самое. Перелистываю на другие главы, и там все в таком же духе. Ну ладно, думаю, почитаю Вернона, там же все-таки о реализации книга. Читаю, и там такая же история. В итоге, прочитал сокращенный вариант - "Domain Driven Design Quickly". Там на ста страницах все рассказано, и даже довольно развернуто, как мне показалось.
В общем, по моим впечатлениям, это книги для разработчиков с низкой квалификацией или малоопытных, чтобы они хоть как-то могли писать код. "Чистый код" и "Чистая архитектура" Роберта Мартина, видимо, им сложно понять.
И в таком случае, эффективность решения будет в разы выше, чем костыли (swoole, gorunner и прочее) на PHP.
Почему же "костыли"? Swoole позволяет выполняться приложению (на Symfony, например) в режиме сервиса. То есть, скрипт постоянно висит в памяти и не выполняется заново каждый запрос, как при FPM. От этого скорость обработки запросов значительно увеличивается.
А вот если говорим про тот же Go (или другой подобный инструмент), то тут сразу понятно, что такой разработчик умеет в асинхронщину, во всякие mutex, потоки, и прочее.
Сам по себе асинхронный код вряд ли даст значительное ускорение. Зато появится много проблем с поддержкой этого всего.
Дак они у вас и сейчас находятся там (правда инфраструктурный слой выше слоя приложения, слой данных вы наверное имели ввиду?), и очень жестко (1 в 1) завязаны на структуре БД и очень вряд ли отражают объекты с точки зрения предметной области.
Везде слой приложения упоминается, как слой, выше инфраструктурного. Можете сами погуглить. Да, ентити отображают БД, который раз уже пишу, и они не отображают объекты с точки зрения предметной области. Предметная область находится в сервисах, и там же объекты, отображающие предметную область.
В том абзаце очень неожиданный вывод, что нужно сделать UserStore, что вообще не вяжется с предметной областью: есть склад, есть товар, который хранится на складе, есть пользователь который ответственный за склад и товары в нем.
Есть Product, содержит в себе Store, есть User, и есть модуль UserStore, отвечающий за взаимодействие User c Store. Так же можно его назвать UserProductStore, если могут быть еще какие-то Store не связанные c Product.
А в вашем варианте получается: есть пользователь, есть склад пользователя, есть товар который хранится на складе пользователя. Получается что склад без пользователя/заведующего не может быть?
Получается, что склад без товара не может существовать. Лучше даже назвать модуль не UserStore, а как-нибудь StoreUser или StoreDirector. Название модуля отражает его функционал и необязательно должно говорить с какими ентити он работает.
Если уж дальше разруливать эту ситуацию, то по хорошему должны быть контексты (в рамках статьи наверное модули):
1. товар, склад и заведующий склада (который никакого отношения не имеет к пользователю, имеет только ид, который в рамках приложения совпадает с ид пользователя)
2. пользователь который вообще никак не сопрекасается со складом и товарам
Контексты находятся в модулях и отделены от ентити через интерфейсы, например. И эти модули могут работать независимо от струтуры БД, ентити. То есть, данные о заведующем можно поместить в User ентити, а можно сделать отдельную. Главное, чтобы она реализовывала StoreDirectorInterface из модуля StoreDirector. И все, модуль ничего не знает о конкретной ентити, работает с ней через интерфейс. Так можно проектировать любую структуру таблиц, независимо от наших модулей. Модуль User так же может ничего не знать об ентити User, он может работать с ней через UserInterface. Но конечно, вы можете отнести ентити User к модулю User, но тогда, модуль StoreDirector уже должен взаимоействовать с целым модулем User, а не только c ентити.
А модуль пользователя и не соприкасается никак со складами и товарами.
В целом плохая идея делать "сложно", чем проще система тем лучше. А делать что-то на будущее бред, поэтому что это будущее может не наступить (и скорее всего не наступит). Поэтому нужно делать ровно то что надо, но оставлять возможно для расширения.
Собственно об этом и речь. Усложняем, если есть в этом необходимость.
Что простите? Дак не забывать или делать? И что такое "по необходимости", когда это оправдано делать god-object "семирукицсемисис" который делает все что угодно? Или наследовать объекты с разным поведением только лишь для переиспользования кода?
Об этом речи не было. Про SOLID я ответил в другом комментарии: "Лучше понимать, что ты делаешь и для чего. Это не универсальные правила, которые всегда подходят, и начинающего могут сбить с толка". Да и вообще, универсальных правил, где учитываются все возможные случаи, не существует, наверное.
А в чем плюс анемичных моделей в сравнении с моделями домена?
Название "анемичная модель" считаю некорректым, так как Entity - это скорее DTO, которое вполне описывает БД, и не соответствует значению слова "анемичность". А преимущества, были упомянуты: "Так мы не привязываем структуру наших классов с логикой к структуре таблиц БД, можем пользоваться DI контейнером, и при необходимости через интерфейсы или DTO можно будет довольно легко отделить логику от конкретной сущности."
Именно так и вы привязываете структуру ваших классов к структуре БД, Entity же отражают структуру таблиц из БД из предложения выше.
Entity можно сделать отдельным низкоуровневым слоем (инфраструктурным). И тогда они будут отделены от слоев выше, где логика приложения, предметная область.
Лучше бы сразу выделять сервис, возвращаясь к вопросу о SOLID и единственной ответственности, который будет работать только с одной сущностью.
С одной сущностью может быть связано много разной логики. Ответственность уже разделяется внутри модуля, если нужно. А модуль берет данные из ентити через инверсию зависимости, например. То есть, Entity реализует интерфейс, который определен в модуле. А в интерфейсе могут быть просто геттеры сеттеры, данные, в общем. Но для простоты разработки можно и не применять инверсию зависимости, думаю, позже сделать ее будет не сложно, если понадобится.
Эээ а зачем? Stores же в любом случае работает с Product и он является его частью. Или у вас склады могут существовать без товаров? Если у вас склад это независимая ни от чего сущность (т.е. может существовать сам собой без User и Product), то выделяйте его в отдельный модуль, иначе (даже по мере роста функционала), где изначально находился сервис там его и оставляется и расширяйте.
Собственно, это и объясняется в том абзаце, только более подробно.
Опять таки, зачем изначально говнокодить? В контроллерах не должно быть бизнес-логики раз уж вы ее в сервисы отправили, иначе вы нарушаете свою же слоистость.
Конечно, делать того, о чем вы говорите не стоит. Но можно туда написать хороший код, и тогда практически одним копипастом вы сможете перенести его в любой класс.
P.S.S. есть подозрение, что вы либо не докрутили, либо целенаправленно не используете старые добрые слои: domain - data - service - infrastructure, и у вас получается кашка из-за этого. Ну и в целом у вас получается этакий DDD (только нет Domain Model (а зря) и разделения на Entity/Aggregate (хотя в целом можно и без него пережить) миксом с CQS (где UseCases это Command)
Названия слоев довольно условны, их может быть больше или меньше, а может и не быть, если модуль небольшой, например. На слои можно делить все приложение и отдельные модули. В основном, информацию по теме архитектуры я получал из книги Роберта Мартина "Чистая архитектура" (оттуда SOLID, UseCase, компоненты, слои), статей в интернете и собственных размышлений, опыта. Позже читал и книги про DDD, но практически ничего полезного там не нашел для себя.
В моем контексте модуль - это какая-то обоснобленная часть кода. А компонент уже полностью отделяется контрактами, возможно, делаются адаптеры. То есть, пишем сначала просто модули без контрактов для удобства. Так и рефакторить проще. А потом при необходимости отделяем все интерфейсами, думаю, здесь не должно быть проблем. Во многих случаях и не потребуется ничего отделять. Но конечно, если мы уже заранее знаем, что наш модуль может понадобиться отделить, то проще сразу это сделать.
Какого-то конкретного критерия по объему не вижу, нужно смотреть, насколько легко его поддерживать, нужно ли разворачивать эти модули отдельно, и насколько связана у них логика. Если два модуля очень сильно взаимодействуют, возможно их не следует разделять. Здесь уже надо от конкретной ситуации отталкиваться.
Да, это один вариант, когда сущности это отдельный слой и относятся ко многим модулям, возможно, отделены интерфейсами. Я так и делаю. Другой вариант, это относить сущность к определенному модулю. А если другому модулю понадобится эта сущность, то он уже будет зависеть от того модуля (ну или инверсия зависимости, адаптеры). Но это уже будет зависимость от целого модуля, первый вариант мне кажется пердпочтительней.
Кстати, Controller, возможно, и легко поместить в модуль. А с Entity посложнее. Но если делать зеркальную струкутру папок, то всегда их можно легко объеденить при необходимости. А можно не объединять, оставить Entity в приложении, и наследовать ее от класса или трейта с интерфейсами, которые будут размещены в модуле. Но в общем случае проще рассматривать Entity как часть модуля, чтобы не городить сразу кучу итнтерфейсов, абстракций, их можно будет внедрить позже, если потребуется выделить компонент.
Цель обозначена в первом абзаце статьи. Я попытался коротко осветить некоторые варианты решения вопросов, с которыми сам столкнулся. А полностью все вопросы покрыть довольно сложно, и на них может и не быть универсального ответа.
Для новичка должно быть догмой - пишем по SOLID и точка
Лучше понимать, что ты делаешь и для чего. Это не универсальные правила, которые всегда подходят, и начинающего могут сбить с толка.
Не представляю, как VK стал бы таким крутым проектом, если бы там был плохой код.
В Ispmanger реализована установка разных версий PHP. Также в Manjaro linux можно устанавливать другие версии через aur репозиторий.
По бенчмарку Symfony на Swoole веб-сервере быстрее аналога Spring из Java - https://www.techempower.com/benchmarks/. Ну и судя по статье - https://www.kode-krunch.com/2021/12/trying-out-php-after-7-years.html, превосходит его по удобству использования и быстроте разработки.
По своему опыту знаю, PHP очень гибкий, быстрый, удобный, его можно использовать практически для чего угодно. Наиболее развита сейчас, конечно, сфера применения в вэбе, бэкенде мобильных приложений. Возможно, Symfony, лучшее, что есть сейчас в своем роде.
Часто незнающие люди говорят, что вот, столько всякого плохого кода написано на PHP, и значит это плохой язык. Но они путают причину и следствие. PHP в свое время набрал огромную популярность, как раз потому, что на нем можно было проще и быстрее всего создавать проекты. Мало кто знает, но у всеми известного Битрикса была так же версия на C#, но она не обрела такой популярности, и код там еще хуже, и поддерживать его еще сложнее. То есть, PHP, как раз, помог в этом случае всем.
Сначала я читал Эванса. Читаю, что-то воды много, думаю, ну может это такое введение долгое. Читаю дальше, то же самое. Перелистываю на другие главы, и там все в таком же духе. Ну ладно, думаю, почитаю Вернона, там же все-таки о реализации книга. Читаю, и там такая же история. В итоге, прочитал сокращенный вариант - "Domain Driven Design Quickly". Там на ста страницах все рассказано, и даже довольно развернуто, как мне показалось.
В общем, по моим впечатлениям, это книги для разработчиков с низкой квалификацией или малоопытных, чтобы они хоть как-то могли писать код. "Чистый код" и "Чистая архитектура" Роберта Мартина, видимо, им сложно понять.
Почему же "костыли"? Swoole позволяет выполняться приложению (на Symfony, например) в режиме сервиса. То есть, скрипт постоянно висит в памяти и не выполняется заново каждый запрос, как при FPM. От этого скорость обработки запросов значительно увеличивается.
Сам по себе асинхронный код вряд ли даст значительное ускорение. Зато появится много проблем с поддержкой этого всего.
Symfony может работать через Swoole: https://www.swoole.co.uk/article/symfony-swoole
При этом он быстрее Spring работает: https://www.techempower.com/benchmarks/#section=data-r20&hw=ph&test=fortune
Так что, не понятно, в чем, собственно, проблема.
Везде слой приложения упоминается, как слой, выше инфраструктурного. Можете сами погуглить. Да, ентити отображают БД, который раз уже пишу, и они не отображают объекты с точки зрения предметной области. Предметная область находится в сервисах, и там же объекты, отображающие предметную область.
Есть Product, содержит в себе Store, есть User, и есть модуль UserStore, отвечающий за взаимодействие User c Store. Так же можно его назвать UserProductStore, если могут быть еще какие-то Store не связанные c Product.
Получается, что склад без товара не может существовать. Лучше даже назвать модуль не UserStore, а как-нибудь StoreUser или StoreDirector. Название модуля отражает его функционал и необязательно должно говорить с какими ентити он работает.
Контексты находятся в модулях и отделены от ентити через интерфейсы, например. И эти модули могут работать независимо от струтуры БД, ентити. То есть, данные о заведующем можно поместить в User ентити, а можно сделать отдельную. Главное, чтобы она реализовывала StoreDirectorInterface из модуля StoreDirector. И все, модуль ничего не знает о конкретной ентити, работает с ней через интерфейс. Так можно проектировать любую структуру таблиц, независимо от наших модулей. Модуль User так же может ничего не знать об ентити User, он может работать с ней через UserInterface. Но конечно, вы можете отнести ентити User к модулю User, но тогда, модуль StoreDirector уже должен взаимоействовать с целым модулем User, а не только c ентити.
А модуль пользователя и не соприкасается никак со складами и товарами.
Собственно об этом и речь. Усложняем, если есть в этом необходимость.
Об этом речи не было. Про SOLID я ответил в другом комментарии: "Лучше понимать, что ты делаешь и для чего. Это не универсальные правила, которые всегда подходят, и начинающего могут сбить с толка". Да и вообще, универсальных правил, где учитываются все возможные случаи, не существует, наверное.
Название "анемичная модель" считаю некорректым, так как Entity - это скорее DTO, которое вполне описывает БД, и не соответствует значению слова "анемичность". А преимущества, были упомянуты: "Так мы не привязываем структуру наших классов с логикой к структуре таблиц БД, можем пользоваться DI контейнером, и при необходимости через интерфейсы или DTO можно будет довольно легко отделить логику от конкретной сущности."
Entity можно сделать отдельным низкоуровневым слоем (инфраструктурным). И тогда они будут отделены от слоев выше, где логика приложения, предметная область.
С одной сущностью может быть связано много разной логики. Ответственность уже разделяется внутри модуля, если нужно. А модуль берет данные из ентити через инверсию зависимости, например. То есть, Entity реализует интерфейс, который определен в модуле. А в интерфейсе могут быть просто геттеры сеттеры, данные, в общем. Но для простоты разработки можно и не применять инверсию зависимости, думаю, позже сделать ее будет не сложно, если понадобится.
Собственно, это и объясняется в том абзаце, только более подробно.
Конечно, делать того, о чем вы говорите не стоит. Но можно туда написать хороший код, и тогда практически одним копипастом вы сможете перенести его в любой класс.
Названия слоев довольно условны, их может быть больше или меньше, а может и не быть, если модуль небольшой, например. На слои можно делить все приложение и отдельные модули. В основном, информацию по теме архитектуры я получал из книги Роберта Мартина "Чистая архитектура" (оттуда SOLID, UseCase, компоненты, слои), статей в интернете и собственных размышлений, опыта. Позже читал и книги про DDD, но практически ничего полезного там не нашел для себя.
В моем контексте модуль - это какая-то обоснобленная часть кода. А компонент уже полностью отделяется контрактами, возможно, делаются адаптеры. То есть, пишем сначала просто модули без контрактов для удобства. Так и рефакторить проще. А потом при необходимости отделяем все интерфейсами, думаю, здесь не должно быть проблем. Во многих случаях и не потребуется ничего отделять. Но конечно, если мы уже заранее знаем, что наш модуль может понадобиться отделить, то проще сразу это сделать.
Какого-то конкретного критерия по объему не вижу, нужно смотреть, насколько легко его поддерживать, нужно ли разворачивать эти модули отдельно, и насколько связана у них логика. Если два модуля очень сильно взаимодействуют, возможно их не следует разделять. Здесь уже надо от конкретной ситуации отталкиваться.
Да, это один вариант, когда сущности это отдельный слой и относятся ко многим модулям, возможно, отделены интерфейсами. Я так и делаю. Другой вариант, это относить сущность к определенному модулю. А если другому модулю понадобится эта сущность, то он уже будет зависеть от того модуля (ну или инверсия зависимости, адаптеры). Но это уже будет зависимость от целого модуля, первый вариант мне кажется пердпочтительней.
Довольно сложно делать здесь графики, есть то, что есть )
Кстати, Controller, возможно, и легко поместить в модуль. А с Entity посложнее. Но если делать зеркальную струкутру папок, то всегда их можно легко объеденить при необходимости. А можно не объединять, оставить Entity в приложении, и наследовать ее от класса или трейта с интерфейсами, которые будут размещены в модуле. Но в общем случае проще рассматривать Entity как часть модуля, чтобы не городить сразу кучу итнтерфейсов, абстракций, их можно будет внедрить позже, если потребуется выделить компонент.
Цель обозначена в первом абзаце статьи. Я попытался коротко осветить некоторые варианты решения вопросов, с которыми сам столкнулся. А полностью все вопросы покрыть довольно сложно, и на них может и не быть универсального ответа.
Лучше понимать, что ты делаешь и для чего. Это не универсальные правила, которые всегда подходят, и начинающего могут сбить с толка.