Для легковесного микросервиса возможно и DDD с CQRS будут избыточными)
В любом случае, спасибо за статью. Вы проделали большую работу и спасибо вам за это. Хорошо, что есть облегченные альтернативы известным крупным проектам.
А если смотреть на ДДД, то в разных ограниченных контекстах разные сущности могут смотреть в одни и те же таблицы
Это тоже решается. Ни кто же не говорит, что сущность доктрины смотрящая на одну таблицу должна быть единственной на весь проекте. Можно создавать доктриновские сущности как вьюхи для таблицы с необходимым набором полей и режимом работы readonly.
в одном сущность менеджер, собирается из таблиц юзер + таблицы для менеджера с его спецификой
В некоторых случаях можно решить вопрос используя связь OneToOne.
Основная проблема доктриновских сущностей, в том, что они позволяют описать не более 3 уровней вложенности (Entity -> Embedded -> Custom Type) без джойнов других сущностей, а в DDD вложенность агрегата может превышать 3 уровня.
Возможно в DoctrineODM нет этих ограничений. Не изучал этот вопрос.
Альтернативный вариант. Вместо ручного маппинга доктриновские сущностей на DDD агрегаты, можно в репозитории писать нативные запросы со всеми джойнами какие нужны, а результат маппить уже на DDD агрегат средствами самой доктрины (да, так тоже можно).
И теперь вы пишете код для маппинга ProductEntity -> Product и обратно. Двойная работа. Два набора классов. Два набора тестов. И постоянный вопрос: «А стоит ли DDD таких накладных расходов?»
Основной мой посыл в том, что в таком виде это стрельба из пушки по воробьям. Если не используется функционал Doctrine, тогда зачем вообще Doctrine. Либо используйте её нормально, либо не используйте вообще.
Doctrine это не просто маппилка таблички из БД на класс в php.
А точно ли нужен двойной маппинг? Doctrine поддерживает ValueObject и кастомные типы.
Ваша Doctrine сущность может иметь вид:
#[ORM\Entity]
#[ORM\Table(name: 'products')]
class Product
{
#[ORM\Id]
#[ORM\Column(type: 'ProductId')]
private readonly ProductId $id;
#[ORM\Column(type: 'ProductName')]
private ProductName $name;
#[ORM\Column(type: 'Money')]
private Money $price;
// ... геттере и сеттере 🤔 Это же не DTO
}
Если хотите воспользоваться объявлением свойств в конструкторе, то придется перенести маппинг в Yaml.
Основной мнус доктриновской сущности, в контексте DDD, это то, что сущность Doctrine нельзя объявить как final. Doctrine использует прокси-классы через наследование для сущностей. Чисто технические ограничения.
Обучение это здорово, НО. Это осмысленно если у вас в компании большая текучка. Это осмысленно если у вас шаблонная работа которую можно регламентировать и стандартизировать. Это работает для продаж и обслуживающего персонала. На позициях где нужно думать и принимать решения по ситуации, обучение будет сводится к обучению пользования инструментария используемого в компании и описание процессов в отделе. Это спорно конечно, но главный критерий внедрения отдельного обучения это большая текучка кадров и только в том случае, если не удаётся решить саму проблему текучки кадров.
В противопоставлении монолита и микросервиса, мне всегда очень нравятся эти тезисы:
В монолите отказ одного модуля часто означает остановку всей системы.
Микросервисы обеспечивают изоляцию сбоев — если один сервис недоступен, остальные продолжают работать.
Они вызывают вопросы:
Что нужно сделать такого, чтобы сбой в работе одного из модулей привёл к полной не работоспособности всего монолита?
Корректно ли считать недоступность одного из сервисов (модулей в случае монолита), нормальной/допустимой работой системы?
Предположим, в проекте отзовиков сломалась подсистема отвечающая за публикацию отзывов. Проект доступен, отзывы доступны, а опубликовать отзыв нельзя. Можно ли считать проект работающим, с точки зрения бизнеса? Не думаю.
Предположим, в интернет магазине сломалась подсистема приёма платежей. Можно смотреть товары, читать отзывы, добавлять товары в корзину, но купить ничего нельзя. Можно ли считать проект работающим, с точки зрения бизнеса? Не думаю.
Предположим, в CRM сломалась авторизация. Все функции системы работают исправно, но пользователи не могут ими воспользоваться потому, что не могут авторизация. Можно ли считать проект работающим, с точки зрения бизнеса? Не думаю.
Подход с использованием брокера очередей для асинхронный обработки задач насчитывает не один десяток лет. Идея не нова.
Использование брокера очередей как шина для взаимодействия с сервисами тоже не нова. По своему интересна, но имеет определённые проблемы.
Плюс в том, что сервис может упасть, а клиент сервиса от этого не страдает, так как не взаимодействует с сервисом напрямую.
Минус в том, что в очередь может попасть событие которое сервис не может обработать (битое, старый формат и т. д.). Что в этому случае делать микро сервису совершенно не понятно. Он не знает ничего о своих клиентах и ему некому сказать о клиентской проблеме.
В противовес этому. Сервис знает какие форматы сообщений он может принимать и если он принял сообщение, то он обязуется его выполнить чтобы не случилось.
По этому, для асинхронной обработки событий сервисом, брокер очередей ставят за сервисом (внутри, за интерфейсом сервиса), а не перед сервисом. При этом интерфейс будет тонким, а вся логика в фоновых процессах. В этом случае, вероятность падения интерфейса сервиса минимальна.
Вопрос масштабирования и производительности тут тоже не актуален, так как развернуть ещё один инстанс сервиса не сложнее чем поднять ещё одну ноду Кафки при грамотной организации инфраструктуры и реализации сервиса.
Проводили ли вы анализ производительности? Например, как отработает запрос со сравнениеи векторов в индексе на 50М докумеетов? Какое максималбное значение похожих документов по векторной близости можно получить?
В декументации сказано:
k The number of nearest neighbors to return from each shard. Elasticsearch collects k results from each shard, then merges them to find the global top results. This value must be less than or equal to num_candidates. Defaults to search request size. num_candidates (Optional, integer) The number of nearest neighbor candidates to consider per shard while doing knn search. Cannot exceed 10,000. Increasing num_candidates tends to improve the accuracy of the final results. Defaults to 1.5 * k if k is set, or 1.5 * size if k is not set.
То есть, на сколькл я понимаю, в не зависимости от количества шардов, ника нельзя получить больше 10 000 докумеетов.
Если кандидат на собеседовании говорит, что ответ 7 при использовании кодировки cp1251, то это как минимум говорит, о том, что он понимает, что в другой кодировке может быть другой результат. +1 кандидату.
А вообще, вопрос можно построить иначе: Что выдаст следующая команда выполненная в среде Linux?
Как раз таки наоборот. При использовании ORM, а точнее UoW в нем, он позволяет обновлять в БД только те сущности и только те поля сущностей которые реально изменились, а не все поля.
Для обеспечения такого же функционала без ORM, придется написать километровый код.
Дополню, что квадрат является не только частным случаем прямоугольника, но и частным случаем ромба. Поэтому в принципе неправильно рассматривать квадрат как частный случай чего-то. Квадрат это самостоятельная фигура со своими, специфичными только для квадрата, свойствами.
Никак. Незачем. Пускай варится в собственном соку и просто предоставляет публичный интерфейс. А если этого недостаточно, то всегда можно сделать адаптер.
Ну resource|false это все таки не union тип. Это микс из типа и одного из значений типа. Если говорить о union, то это будет resource|bool, что подразумевает, что true так же является возможным возвращаемым значением. В этом то основная проблема.
Вчера правил баг в PHPStan и PhpStorm связанный с этим и задумался, а не пора ли наконец привести все в порядок, тем более, что выходит мажорная версия. И править нужно не сказать, что очень много.
А в целом вроде в следующих версиях планируется сначала задепрекейтить, а потом полностью отказаться от таких библиотечных функций, а перевести их на ООП основу с исключениями для ошибок.
А есть какие-то ссылки подтверждающие, что это планируют именно в PHP 8? Разговор об этом ведется уже много лет, еще со времен PHP 5.4 кажутся, но подвижек я как-то не заметил.
Для легковесного микросервиса возможно и DDD с CQRS будут избыточными)
В любом случае, спасибо за статью. Вы проделали большую работу и спасибо вам за это. Хорошо, что есть облегченные альтернативы известным крупным проектам.
Мои комментарии к тому, что сравнительная таблица должна выглядеть так:
Не был бы так категоричен)
Это тоже решается. Ни кто же не говорит, что сущность доктрины смотрящая на одну таблицу должна быть единственной на весь проекте. Можно создавать доктриновские сущности как вьюхи для таблицы с необходимым набором полей и режимом работы readonly.
В некоторых случаях можно решить вопрос используя связь OneToOne.
Основная проблема доктриновских сущностей, в том, что они позволяют описать не более 3 уровней вложенности (Entity -> Embedded -> Custom Type) без джойнов других сущностей, а в DDD вложенность агрегата может превышать 3 уровня.
Возможно в DoctrineODM нет этих ограничений. Не изучал этот вопрос.
Альтернативный вариант.
Вместо ручного маппинга доктриновские сущностей на DDD агрегаты, можно в репозитории писать нативные запросы со всеми джойнами какие нужны, а результат маппить уже на DDD агрегат средствами самой доктрины (да, так тоже можно).
Основной мой посыл в том, что в таком виде это стрельба из пушки по воробьям. Если не используется функционал Doctrine, тогда зачем вообще Doctrine. Либо используйте её нормально, либо не используйте вообще.
Doctrine это не просто маппилка таблички из БД на класс в php.
А точно ли нужен двойной маппинг? Doctrine поддерживает ValueObject и кастомные типы.
Ваша Doctrine сущность может иметь вид:
Если хотите воспользоваться объявлением свойств в конструкторе, то придется перенести маппинг в Yaml.
Основной мнус доктриновской сущности, в контексте DDD, это то, что сущность Doctrine нельзя объявить как
final. Doctrine использует прокси-классы через наследование для сущностей. Чисто технические ограничения.Обучение это здорово, НО. Это осмысленно если у вас в компании большая текучка. Это осмысленно если у вас шаблонная работа которую можно регламентировать и стандартизировать. Это работает для продаж и обслуживающего персонала. На позициях где нужно думать и принимать решения по ситуации, обучение будет сводится к обучению пользования инструментария используемого в компании и описание процессов в отделе. Это спорно конечно, но главный критерий внедрения отдельного обучения это большая текучка кадров и только в том случае, если не удаётся решить саму проблему текучки кадров.
В противопоставлении монолита и микросервиса, мне всегда очень нравятся эти тезисы:
Они вызывают вопросы:
Что нужно сделать такого, чтобы сбой в работе одного из модулей привёл к полной не работоспособности всего монолита?
Корректно ли считать недоступность одного из сервисов (модулей в случае монолита), нормальной/допустимой работой системы?
Предположим, в проекте отзовиков сломалась подсистема отвечающая за публикацию отзывов. Проект доступен, отзывы доступны, а опубликовать отзыв нельзя. Можно ли считать проект работающим, с точки зрения бизнеса? Не думаю.
Предположим, в интернет магазине сломалась подсистема приёма платежей. Можно смотреть товары, читать отзывы, добавлять товары в корзину, но купить ничего нельзя. Можно ли считать проект работающим, с точки зрения бизнеса? Не думаю.
Предположим, в CRM сломалась авторизация. Все функции системы работают исправно, но пользователи не могут ими воспользоваться потому, что не могут авторизация. Можно ли считать проект работающим, с точки зрения бизнеса? Не думаю.
Проводили бенчмарки memcached vs pg vs shmop?
Подход с использованием брокера очередей для асинхронный обработки задач насчитывает не один десяток лет. Идея не нова.
Использование брокера очередей как шина для взаимодействия с сервисами тоже не нова. По своему интересна, но имеет определённые проблемы.
Плюс в том, что сервис может упасть, а клиент сервиса от этого не страдает, так как не взаимодействует с сервисом напрямую.
Минус в том, что в очередь может попасть событие которое сервис не может обработать (битое, старый формат и т. д.). Что в этому случае делать микро сервису совершенно не понятно. Он не знает ничего о своих клиентах и ему некому сказать о клиентской проблеме.
В противовес этому. Сервис знает какие форматы сообщений он может принимать и если он принял сообщение, то он обязуется его выполнить чтобы не случилось.
По этому, для асинхронной обработки событий сервисом, брокер очередей ставят за сервисом (внутри, за интерфейсом сервиса), а не перед сервисом. При этом интерфейс будет тонким, а вся логика в фоновых процессах. В этом случае, вероятность падения интерфейса сервиса минимальна.
Вопрос масштабирования и производительности тут тоже не актуален, так как развернуть ещё один инстанс сервиса не сложнее чем поднять ещё одну ноду Кафки при грамотной организации инфраструктуры и реализации сервиса.
Проводили ли вы анализ производительности? Например, как отработает запрос со сравнениеи векторов в индексе на 50М докумеетов? Какое максималбное значение похожих документов по векторной близости можно получить?
В декументации сказано:
То есть, на сколькл я понимаю, в не зависимости от количества шардов, ника нельзя получить больше 10 000 докумеетов.
Если кандидат на собеседовании говорит, что ответ 7 при использовании кодировки cp1251, то это как минимум говорит, о том, что он понимает, что в другой кодировке может быть другой результат. +1 кандидату.
А вообще, вопрос можно построить иначе: Что выдаст следующая команда выполненная в среде Linux?
JSONL например
Хм. В первые слышу об этом формате. В подобных случаях обычно используется формат JSONL.
balance: Float?В опасные игры вы играете)
Как раз таки наоборот. При использовании ORM, а точнее UoW в нем, он позволяет обновлять в БД только те сущности и только те поля сущностей которые реально изменились, а не все поля.
Для обеспечения такого же функционала без ORM, придется написать километровый код.
Дополню, что квадрат является не только частным случаем прямоугольника, но и частным случаем ромба. Поэтому в принципе неправильно рассматривать квадрат как частный случай чего-то. Квадрат это самостоятельная фигура со своими, специфичными только для квадрата, свойствами.
Никак. Незачем. Пускай варится в собственном соку и просто предоставляет публичный интерфейс. А если этого недостаточно, то всегда можно сделать адаптер.
Пользуюсь сервисом уже лет 6-7 и тоже оценка обычно держится в районе 9-10)
Советую еще посмотреть в сторону статических анализаторов типа PHPStan и Psalm.
Ясно. Спасибо. Значит продолжаем ждать.
Ну
resource|falseэто все таки не union тип. Это микс из типа и одного из значений типа. Если говорить о union, то это будетresource|bool, что подразумевает, чтоtrueтак же является возможным возвращаемым значением. В этом то основная проблема.Вчера правил баг в PHPStan и PhpStorm связанный с этим и задумался, а не пора ли наконец привести все в порядок, тем более, что выходит мажорная версия. И править нужно не сказать, что очень много.
А есть какие-то ссылки подтверждающие, что это планируют именно в PHP 8? Разговор об этом ведется уже много лет, еще со времен PHP 5.4 кажутся, но подвижек я как-то не заметил.