Анализ Use Cases взаимодействия с модулем аутентификации. Выделение команд Command и запросов Query. Принцип CQS разделения операций.
- 00:00:18 Постановка задачи
- 00:00:47 Структура директорий
- 00:02:49 Перемещение тестов
- 00:05:32 Анализ бизнес-процессов
- 00:06:54 Взаимодействие с модулем
- 00:10:38 Запросы чтения и модификации
- 00:11:32 Принцип CQS про Command и Query
- 00:14:00 Структура и юзкейсы
- 00:16:23 Папка модуля
- 00:17:30 Юзкейсы модуля аутентификации
- 00:22:46 Запуск операций из разных источников
- 00:24:51 Команда регистрации
- 00:28:31 Подведение итогов
Скрытый контент (код, слайды, ...) для подписчиков.
Открыть →Чтобы не пропускать новые эпизоды подпишитесь на наш канал @deworkerpro в Telegram
Спасибо. Очень интересно.
Спасибо, Дмитрий! Хотел бы посмотреть в вашем исполнении на реализацию подобной ситуации: пользователь регистрируется в контексте аутентификации с ролью покупателя, в контексте покупателей также создается сущность с данными покупателя. Эти данные относятся только к предметной подобласти покупателей ( и не относятся к контексту аутентификации ). Ситуация гиппотетическая. Ну и вообще очень интересно было бы посмотреть на различные вариации интеграции ограниченных контекстов.
Это вполне реальная ситуация, у покупателя куча полей, которые не нужны пользователю, например в битрикс так сделано, думаю Дмитрий поступит аналогично, когда дойдет до аукциона.
Спасибо. Хотелось бы чуть сторонней информации о процессе "проверка приложения" на Facebook. И еще предположим, что нам ТРЕБУЕТСЯ майл пользователя, а Facebook API не всегда его предоставляет, выдает токин и всё, что будем делать в таком случае?
Запрашивать почту дополнительно.
Это был намек на добавления этого процесса.
Заметил, что изменение скорости воспроизведения видео не работает. Смотрю с мобильного браузера chrome android
Непонятно почему DTO назвали Command, ведь Command должен что-то изменять, а DTO объект просто содержит данные.
Есть команда на изменение и её обработчик. Мы отправляем команду с описанием того, что нам нужно сделать. И система её выполняет. Такое именование используется в подходе с Command Bus, где команды даже могут сериализоваться и отправляться в очередь для выпонления в фоне.
Это не классический паттерн Command, где сам объект команды содержит методы execute() и undo(). У нас такой подход будет неудобен, так как обработчику понадобятся зависимые объекты и его конструктор будет ими занят.
будет ли очередь (Rabbit или Queue) ?
В описании курса написано "С WebSocket-интерактивом и очередями на RabbitMQ"!
да да, спс., незаметил ..:)
Если именование Handle ещё не очень критично, так как мы его фактически нигде не вызываем, обычно это command bus, то вот с Command все сложнее. По всему коду и тестам у нас будут new Command(), чтобы понять что это за команда, надо либо полезть в use, либо делать в use алиас, либо перейти в класс команды. Всё это неудобно. Не лучше ли избавиться от папки c названием команды и перенести это в название класса, например, JoinByEmailRequestCommand, JoinByEmailRequestHandler. С одной стороны длинные названия классов, но с другой стороны в сто раз код читабельнее становится
Можно как угодно. Становится читабельнее, но не писабельнее. А так да, если вдруг потребуется использовать две команды в одном классе (что вряд ли произойдёт), то можно использовать алиасы.
Очень полезный урок, спасибо.
Хотелось бы узнать а как транзакции реализуются при таком подходе, предположим зарегистрировали пользователя (пользователь нужен сразу в контексте авторизации, автора и покупателя), это посути записи в три отдельных контекста.
Пользователь валидный только если он присутствует во всех трёх контекстах одновременно (по разным причинам).
Нам по сути по одному действию (регистрации) нужно кинуть три команды сразу. Две у нас выполняется, третья крашится. По условию пользователь не валидный, что делать в таком случае? В транзакцию это дело не завернешь, ну и две предыдущие нужно откатить как то ?
Это делается через саги, подписываемые на доменные события.
Ну погодите, как это это через саги?
Мы меняем агрегат (по которому границы транзакционности) в хэндлере, нужно это делать в транзакции в этом же хэндлере!
Единственное, там могут появиться посредники, которые это на себя возьмут, но все же или я не прав?
Саги как раз и придуманы для осуществления таких транзакций нескольких агрегатов из нескольких контекстов с откатом изменений при ошибке.
Сага напрямую или по событию регистрации отправляет эти команды во все модули. И в случае сообщения об ошибке от любого модуля компенсирующими командами откатывает все предыдущие.
Спасибо за уроки. Появилось пару вопросов. "Модуль" Auth это конекст из ддд терминологии? Содержимое папки Commands это Application Services или Domain Services?
Да, разбиваем на модули по контекстам. А горизонтальное разбиение такое:
Здравствуйте. Спасибо за толковый подход!) А почему структура текущего проекта не такая? Она избыточна?
И ещё один вопрос интересный. Есть мероприятия, на которые регистрируются участники. Сейчас это модуль мероприятий. Но регистрации бывают разных типов и у каждого типа своя регистрация. Я разделил все так:
Event/Registrations/Competition
Event/Registrations/Battle
... другие регистрации (приобретение билетов....)Правильно ли я сделал? Либо регистрацию вынести в отдельный модуль. А в самом модуле только использовать ID мероприятия.
Ещё, как вариант, разделить Event по типам:
Event/Competition/Registration
Event/Battle/Registration
И тогда создание мероприятий будет в разных сущностях и хранится в разных таблицах. В яндекс афише тоже есть подобное. И, как мне кажется, там всё разделено. Даже приобретение билетов...
Если делать полное разбиение по слоям, то нужно ещё и отделить слой инфраструктуры:
в который вынести всю работу с Doctrine и прочие "грязные" реализации, а в Domain оставить только чистые сущности и чистые доменные интерфейсы.
Так что да, такое горизонтальное разбиение на папки часто избыточно.
Видел такое разбиение у вас вhttps://github.com/ElisDN/rabbit-demo-spa. Сейчас проект и подход похожий, поэтому спросил) Понял. Спасибо за ответ!)
Hello, can you provide a complete framework (folder) template containing three buses (command, query, event), thank you!
Full structure will be complete in this auction project.
Доброго времени суток. Спустя время появился вопрос на тему архитектуры текущей архитектуры. Появились библиотеки по контролю за архитектурой, например, https://github.com/qossmic/deptrac и сразу появилось два вопроса:
Искусственное деление на такие слои может быть оправдано внутри модуля или простого приложения. И папка инфраструктуры будет полезна, если мы явно отделяем интерфейсы в домене от реализаций в инфраструктуре.
В большом проекте простое раскладывание всех классов горизонтально на три папки проследит за обращением по слоям вниз, но не спасёт от обращения в бок, когда application-сервис блога вдруг обратится к domain-сущности магазина. А избавление от лишних связей между модулямии полезнее, чем между слоями.
Верно. Мы раскладываем классы по модулям и компонентам, а не растаскиваем на папки по типу. Отдельно горизонтально у нас вынесен в свою папку только слой Http.
У нас в папке компонента лежат вместе все его классы и интерфейсы. Например, в компоненте FeatureToggle у нас три интерфейса для использования в разных местах, класс с реализацией и Twig-расширение. И даже если внутри модуля есть отдельные папки для команд и сущностей, то всё равно классы доменных сущностей и репозиториев лежат вместе с инфраструктурными Doctrine-типами.
Учитывая вышесказанное, для полноценного использования deptrac в нашем проекте в его конфигурации придётся буквально вручную описывать каждый класс и интерфейс для компонентов. И для каждого модуля вычленять все команды, репозитории и типы регулярными выражениями по суффиксам классов. Тогда будет полный контроль, но много усилий уйдёт на конфигурирование сотен классов и правил.
Но как компромисс мы можем использовать его поверхностно, объявляя в нём только папки целого компонента или модуля вроде Auth и Mailer.
Так что по деления на папки всегда есть выбор, что либо мы делаем как удобно нам, либо приходится делать как удобно какому-то фреймворку или библиотеке.
Если регистрация производится одинаково, то вынести в Registration c EventId.
Если на Competition и Battle по-разному регистрируются, то проще каждому сделать свою отдельную регистрацию Event/Competition/Registration и Event/Battle/Registration.
Да, регистрации разные. Кроме того, есть система подсчета результатов для каждого события.
В итоге получается:
Event/Competition/Registration
Event/Competition/Result
Event/Battle/Registration
Event/Battle/Result
Вот и подумал, может это вынести в отдельные системы, как вы говорили про склад и другие...
Дмитрий, скажите, пожалуйста, если проект у нас полностью на yii2 (advanced) : frontend, backend, api - тогда CQS оставляем в папке апи, а сущности выносим в какую-нибудь папку, например, auction? То есть структура будет такой:
Верно размышляю?
Да, в этом случае делаем отдельную папку
auction
, а в остальных папках осталяем контролеры и представления.Кстати. Дмитрий. Нашел очень хороший сервис Auth0 бесплатный тариф до 7000 АКТИВНЫХ пользователей. Отлично подходит на микросервисы. Почему не использовать бы его в своих проектах и не писать каждый раз свой Auth. Понимаю, что мы учимся и Auth может быть со своими требованиями, но не плохой сервис как по мне. Что скажете?)
Да, сервис популярный. Но управление ролями и связывание соцсетей есть только в платном тарифе, который доходит до $1k в месяц. Если нужна двухфакторная аутентификация по SMS, то с хранением телефона на их сервере вероятно нарушается требование о хранении персональных данных в РФ. Амазоновский IP-адрес может случайно попасть под блокировку РКН. Ну и для активной работы всё равно может потребоваться кастомная интеграция. Так что нюансов тоже много.
Интересно. Приоткрыли глаза) Жаль. Сервис хороший. Не так сильно его изучал, но теперь буду осторожнее с ним. Думал, что это может быть хорошим решением. Хотелось услышать ваше мнение. Как всегда много полезного! Спасибо ещё раз!
Здравствуйте) Последняя тенденция модуль Auth называть Id (Identity). Например, Сбербанк ID, DeworkerPro ID. Может быть в этом проекте тоже назвать данный модуль так? Или я не так понимаю? Заодно будет ролик про то, когда меняется что Глобальное)
Да, можно пойти по примеру OpenID и назвать Identity и вынести на поддомен id.xxx. Это дело вкуса.
Здравствуйте
Спасибо, отличный материал
На основе этой архитектуры планирую сделать телеграм бот на node js (telegraf.js).
То есть человек заходить отправляет токен бота и ему поднимается телеграм бот новый инстанс
через React админку можно включать и отклать этот бот. Как лучше реализовать ?
Да, из фронтенда на React по API и WebSocket общаться с бэкендом на Node JS вместо PHP.
Немного не понял про Use Case. Это же по сути сервис? Т.е с тем же успехом можно было сделать сервис хранящий бизнес-логику, или я что-то путаю?
Про это дополнительно рассказал в статье про объекты в своём блоге.
Или войти через: