Проектирование сущностей, сервисов и репозиториев

Обсуждаем, как правильно строить приложения
sda
Сообщения: 332
Зарегистрирован: 2013.12.19, 09:29

Re: Проектирование сущностей, сервисов и репозиториев

Сообщение sda » 2017.10.13, 06:56

ElisDN это через eventual consistency решается же. Заказ сохраняем сейчас, всё остальное делаем чуть позже по доменному событию OrderCreated. Хотя какое-то подобие транзакций всё же потребуется, чтобы атомарно сохранить агрегат + доменное событие. Но если это event sourcing, то атомарно сохранять нужно будет только доменное событие.

Чем больше агрегатов сохраняется внутри одной транзакции тем выше вероятность, что такая транзакция не закоммитится с первого раза из-за оптимистических блокировок. Чем выше трафик, тем медленнее будет работать такая система.

zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Проектирование сущностей, сервисов и репозиториев

Сообщение zelenin » 2017.10.13, 09:47

sda писал(а):
2017.10.13, 06:56
Хотя какое-то подобие транзакций всё же потребуется, чтобы атомарно сохранить агрегат + доменное событие
чтобы отправить событие в шину, сохранять его не надо

sda
Сообщения: 332
Зарегистрирован: 2013.12.19, 09:29

Re: Проектирование сущностей, сервисов и репозиториев

Сообщение sda » 2017.10.13, 17:20

zelenin но после сохранения агрегата и до отправки события в шину может произойти сбой. Событие потеряется.

anton_z
Сообщения: 419
Зарегистрирован: 2017.01.15, 15:01

Re: Проектирование сущностей, сервисов и репозиториев

Сообщение anton_z » 2017.10.14, 03:57

sda писал(а):
2017.10.13, 17:20
zelenin но после сохранения агрегата и до отправки события в шину может произойти сбой. Событие потеряется.
Да, почему-то многие про это не думают. Даже странно. Есть, конечно, паттерны, где сначала событие ставится в персистентную очередь, а потом уже производятся изменения в хранилище, но пока не видел возможности реального применения.

По поводу списания с баланса по событиям (Eventual Consistency) - это все очень опасно. Допустим у вас пиковая нагрузка и все воркеры заняты, ну или вообще воркеры недоступны по какой-то причине (сеть, сервер и пр.). Пользователь производит оформление, а событие еще не обработано. Пользователь может увидеть, что деньги не списались и оформить еще один или несколько заказов до первого списания. Как будете разруливать? Как объяснить пользователю, почему система дала ему оформить еще один заказ? Транзакции прекрасно решают эту задачу. Без них решить ее может и можно, но лекарство окажется хуже болезни - сложно все станет. Интернет-магазины не такие уж сложные приложения, нет там такой логики, чтобы DDD применять. Они прекрасно делались и работали до изобретения всех этих DDD и ES.

Аватара пользователя
maleks
Сообщения: 1726
Зарегистрирован: 2012.12.26, 12:56

Re: Проектирование сущностей, сервисов и репозиториев

Сообщение maleks » 2017.10.14, 06:31

anton_z , а это не ваша статейка на хабре появилась?

anton_z
Сообщения: 419
Зарегистрирован: 2017.01.15, 15:01

Re: Проектирование сущностей, сервисов и репозиториев

Сообщение anton_z » 2017.10.14, 10:06

maleks писал(а):
2017.10.14, 06:31
anton_z , а это не ваша статейка на хабре появилась?
Да, моя статейка)


zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Проектирование сущностей, сервисов и репозиториев

Сообщение zelenin » 2017.10.14, 14:56

удивлен низким качеством статьи, особенно в плане теории.
В частности описанием минусов дата маппера на примере доктрины, которые вообще-то являются минусами конкретной ОРМ, которая где-то внутри использует паттерн Дата маппер.
Дата маппер - это тупо преобразователь полученных из БД данных в модель предметной области. Какие у него могут быть минусы?)

anton_z
Сообщения: 419
Зарегистрирован: 2017.01.15, 15:01

Re: Проектирование сущностей, сервисов и репозиториев

Сообщение anton_z » 2017.10.14, 15:05

Doctrine ORM является реализацией DataMapper + UoW. Но дело не только в нем. Плох сам подход к такому преобразованию - без участия самого объекта, с использованием рефлексии, манипуляций с объектом, как со структурой данных.

zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Проектирование сущностей, сервисов и репозиториев

Сообщение zelenin » 2017.10.14, 15:11

anton_z писал(а):
2017.10.14, 15:05
Doctrine ORM является реализацией DataMapper + UoW
нет, она является реализацией ОРМ, где внутри используется дата маппер и куча другого обвеса.
anton_z писал(а):
2017.10.14, 15:05
Но дело не только в нем. Плох сам подход к такому преобразованию - без участия самого объекта, с использованием рефлексии
опять же это не проблема дата маппера, а проблемы (на ваш взгляд проблемы) конкретной реализации. Маппить данные можно как угодно, хоть с рефлексией, хоть без.
anton_z писал(а):
2017.10.14, 15:05
манипуляций с объектом, как со структурой данных.
ок, можно еще раз повторить тезис (статья достаточно обширная) - почему плох подход с рефлексией и как по другому маппить?

anton_z
Сообщения: 419
Зарегистрирован: 2017.01.15, 15:01

Re: Проектирование сущностей, сервисов и репозиториев

Сообщение anton_z » 2017.10.14, 15:19

Никак не маппить. Обращаться к базе за данными как к внешнему ресурсу (чем она и является) через подключение. Инкапсулировать эти обращения в нескольких классах (для множеств и для конкретных единиц данных).

zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Проектирование сущностей, сервисов и репозиториев

Сообщение zelenin » 2017.10.14, 15:20

anton_z писал(а):
2017.10.14, 15:19
Никак не маппить. Обращаться к базе за данными как к внешнему ресурсу (чем она и является) через подключение. Инкапсулировать эти обращения в нескольких классах (для множеств и для конкретных единиц данных).
ну так работать ты с чем будешь? с массивом скаляров или с объектом сущности?

anton_z
Сообщения: 419
Зарегистрирован: 2017.01.15, 15:01

Re: Проектирование сущностей, сервисов и репозиториев

Сообщение anton_z » 2017.10.14, 15:22

с DAO/шлюзами к строкам

sda
Сообщения: 332
Зарегистрирован: 2013.12.19, 09:29

Re: Проектирование сущностей, сервисов и репозиториев

Сообщение sda » 2017.10.15, 05:20

anton_z мой интерес направлен на распределенные системы и nosql. Чем-то необходимо жертвовать. Да и заказ можно собирать пока пользователь выбирает товары. Тогда к моменту оплаты заказ будет готов. Списываем деньги сейчас, а статус заказа и всё остальное поменяем чуть позже. На клиенте можно даже сразу показать, что заказ уже оплачен и тогда пользователь вообще ничего не заметит.
Транзакции прекрасно решают эту задачу.
Скорее амазон прекрасно бы не работал. Я вот не хочу использовать acid/джойны/связи из рсубд и знать, что хранилище теперь горизонтально не масштабируется дальше одной машины. Затем это всё равно всё надо выпиливать и писать свой шардинг для рсубд. Я пытаюсь разобраться как делают горизонтально масштабируемые системы. На какие компромиссы идут.


anton_z
Сообщения: 419
Зарегистрирован: 2017.01.15, 15:01

Re: Проектирование сущностей, сервисов и репозиториев

Сообщение anton_z » 2017.10.16, 05:10

sda писал(а):
2017.10.15, 05:20
Скорее амазон прекрасно бы не работал. Я вот не хочу использовать acid/джойны/связи из рсубд и знать, что хранилище теперь горизонтально не масштабируется дальше одной машины. Затем это всё равно всё надо выпиливать и писать свой шардинг для рсубд. Я пытаюсь разобраться как делают горизонтально масштабируемые системы. На какие компромиссы идут.
Решние, где не надо ничего придумывать в коде приложения - синхронная мультимастер репликация. Естественно со своими болячками в виде задержек синхронизации и требованиями к одинаковой производительности железа на всех нодах. Полностью синхронная репликация есть в MySQL NDB Cluster - он платный. Есть еще Galera, но там в общем-то асинхронная репликация по большому счету. NDB Cluster сам не использовал, честно скажу, и не знаю никого, кто бы использовал. Просто слышал. Может вам эта информация поможет.
Последний раз редактировалось anton_z 2017.10.20, 05:27, всего редактировалось 1 раз.

sda
Сообщения: 332
Зарегистрирован: 2013.12.19, 09:29

Re: Проектирование сущностей, сервисов и репозиториев

Сообщение sda » 2017.10.16, 06:32

anton_z ну меня интересует возможность шардировать базу и при этом как-то сохранить согласованность данных. Думал реализовать паттерн Saga с двумя задачами сохранить агрегат в базу и отправить доменное событие в очередь. Саму сагу сделать персистентной и при рестарте завершать все незавершенные саги. Но может быть ситуация когда два параллельных процесса изменили агрегат и одна сага его уже сохранила, а вторая еще нет. Затем всё падает. При рестарте неизвестно, кто из двух саг уже успел изменить агрегат и какую из них надо завершить, а какую откатить. Идея очевидная, но чтобы контекст разговора не расходился можно посмотреть https://www.youtube.com/watch?v=xDuwrtwYHu8

Другой подход это архитектура CQRS + ES. Но я вижу ряд проблем. Основная из которых это то, что перепроектирование любого агрегата ведет к поломке потока событий. Это как забрать право на ошибку у программиста. Единственное решение, которое я нашел это брать текущий поток событий и перегонять его в новый поток событий в книге Грега, которую он почему-то так и не дописал. Остальные, те кто используют и рассказывают о event sourcing и как они его применили на своем проекте, почему то предпочитают не отвечать на подобные вопросы.

Других подходов к шардированию и согласованности данных как-то особо не нашел. Ну не считая two phase commit и newSQL баз данных из которых пока ничего зарекомендовавшего себя нет кроме google spanner, что не для простых смертных. Хотя по документации orientDB выглядит круто. Но стоит почитать тех, кто её использовал и ловишь дикий хейт. Учитывая тот момент, что за 7 лет так и не взлетела.

Аватара пользователя
maleks
Сообщения: 1726
Зарегистрирован: 2012.12.26, 12:56

Re: Проектирование сущностей, сервисов и репозиториев

Сообщение maleks » 2017.10.17, 15:58

anton_z писал(а):
2017.10.14, 10:06
Да, моя статейка)
За холиваром DM vs AR там, остался без внимания ваш тот рецепт насчет AR. Я его прочел и вот не понял - это у вас ваш рабочий "боевой" вариант или просто вы предполагаете что этим способом будет лучше? Сложно зацепиться там за мысли. Вы делаете некое утверждение, но не обосновываете как вы к нему пришли.

anton_z
Сообщения: 419
Зарегистрирован: 2017.01.15, 15:01

Re: Проектирование сущностей, сервисов и репозиториев

Сообщение anton_z » 2017.10.19, 15:36

maleks писал(а):
2017.10.17, 15:58
За холиваром DM vs AR там, остался без внимания ваш тот рецепт насчет AR. Я его прочел и вот не понял - это у вас ваш рабочий "боевой" вариант или просто вы предполагаете что этим способом будет лучше? Сложно зацепиться там за мысли. Вы делаете некое утверждение, но не обосновываете как вы к нему пришли.
Да, с недавнего времени это рабочий вариант. Как к нему пришли? Рефакторили большой и старый проект на Yii 1, где все на все завязано, монолитно и с разделением обязанностей все плохо. Была поставлена задача переписать UI. Заодно решили попробовать отделить UI и работу с данными. Попробовали закрыть AR интерфейсами, чтобы между Ui и логикой приложения с данными появился четко определенный интерфейс. Бизнес-логику от данных отделять не стали, так как это какая-то неоправданная задача (да и с AR нерешаемая). Пришли к тому, что с AR работать не так уж плохо, нужно соблюдать такие вот правила:

1. Создание экземпляров делать в классе, который принадлежит к персистентному слою - $user = $users->add($email, $name .....). Позволяет закрыть AR интерфейсом и мокать/декорировать ее. Класс для $user может не наследовать от AR, а включать объект AR, что даст больше гибкости и освободит конструкторы - станет возможной инжекция в конструктор. DAO к множетсву юзеров инжектится через контейнер и также закрыт интерфейсом.
2. Создание экземпляра AR это связанные операции. Из метода добавления add() не должна выходить AR, не предвтавляющая реальную сроку в БД. На то она и record, чтобы представлять запись. В методе add() лучше избегать бизнес-логики - только вставка. Все необходимое рассчитывать в клиентских классах.
3. Не использовать метод save(). Все изменения в БД производить сразу при обращении к методам объекта, типа $user->rename($name), $promocode->activate() и пр. Эти операции должны быть самодостаточными, и должны быть атомарными с точки зрения модификации данных в БД.
4. С реляциями работать также как в п.3 $user->addPhone(), $user->removePhone(). Никаких withRelated и сборки графа из AR в памяти и сохранения одним save(). Если нужно сначала все в памяти - тогда DM + программный UoW.
5. Для объединения нескольких операций и атомарности между ними использовать транзакции (тот же UoW, только на стороне БД). Оборачивать последовательные вызовы в транзакцию.
6. Никакой встренной валидации AR в слое работы с данными - валидация форм в классах форм, В методах объектов с бизнес-логикой и данными - исключения. Никаких сценариев.
7. Из DDD взяли себе на вооружение только доменные события - они очень удобны для уведомлений, аудита, репликации в индексы.
8. Естественно, никаких Yii:app().

Думали и про Doctrine ORM. так как у меня был опыт использования ORM и ODM в других проектах. После пробного переписывания одного модуля, стало понятно что ясности это не добавило, а добавило неявность, больше мест (маппинги, кастомные типы) для редактирования и убавило скорость отклика.

Все получается. Ну, провайдеры и data-виджеты выкинули, да без них и проще. Используем Twig. Теперь по возможности, будем переписывать слой логики и данных на AR из Yii2, Eloquent или на что-то другое, что окажется удобнее (если подберем).

Для наших задач оказалось нормально, все становится понятнее. Очень не хватает метода resetRelation() для сброса загруженных связей в AR.
Последний раз редактировалось anton_z 2017.10.20, 02:37, всего редактировалось 6 раз.

anton_z
Сообщения: 419
Зарегистрирован: 2017.01.15, 15:01

Re: Проектирование сущностей, сервисов и репозиториев

Сообщение anton_z » 2017.10.20, 02:27

maleks писал(а):
2017.10.17, 15:58
Сложно зацепиться там за мысли. Вы делаете некое утверждение, но не обосновываете как вы к нему пришли.
Вы знаете, я статьи вообще не очень-то люблю писать, но вот тут решился поделиться с сообществом и получить конструктивные замечания, быть может, я что-то упустил.

Закрыто