Use Cases модуля аутентификации

Анализ 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 - Подведение итогов
Скрытый контент (код, слайды, ...) для подписчиков. Открыть →
Дмитрий Елисеев
elisdn.ru
Комментарии (40)
Arunas

Спасибо. Очень интересно.

Ответить
Bondarenko Alexandr

Спасибо, Дмитрий! Хотел бы посмотреть в вашем исполнении на реализацию подобной ситуации: пользователь регистрируется в контексте аутентификации с ролью покупателя, в контексте покупателей также создается сущность с данными покупателя. Эти данные относятся только к предметной подобласти покупателей ( и не относятся к контексту аутентификации ). Ситуация гиппотетическая. Ну и вообще очень интересно было бы посмотреть на различные вариации интеграции ограниченных контекстов.

Ответить
fedot

Это вполне реальная ситуация, у покупателя куча полей, которые не нужны пользователю, например в битрикс так сделано, думаю Дмитрий поступит аналогично, когда дойдет до аукциона.

Ответить
Ruslan

Спасибо. Хотелось бы чуть сторонней информации о процессе "проверка приложения" на Facebook. И еще предположим, что нам ТРЕБУЕТСЯ майл пользователя, а Facebook API не всегда его предоставляет, выдает токин и всё, что будем делать в таком случае?

Ответить
kafkiansky

Запрашивать почту дополнительно.

Ответить
Ruslan

Это был намек на добавления этого процесса.

Ответить
Александр Кулик

Заметил, что изменение скорости воспроизведения видео не работает. Смотрю с мобильного браузера chrome android

Ответить
Алексей

Непонятно почему DTO назвали Command, ведь Command должен что-то изменять, а DTO объект просто содержит данные.

Ответить
Дмитрий Елисеев

Есть команда на изменение и её обработчик. Мы отправляем команду с описанием того, что нам нужно сделать. И система её выполняет. Такое именование используется в подходе с Command Bus, где команды даже могут сериализоваться и отправляться в очередь для выпонления в фоне.

Это не классический паттерн Command, где сам объект команды содержит методы execute() и undo(). У нас такой подход будет неудобен, так как обработчику понадобятся зависимые объекты и его конструктор будет ими занят.

Ответить
Arunas

будет ли очередь (Rabbit или Queue) ?

Ответить
Дмитрий Ориховский

В описании курса написано "С WebSocket-интерактивом и очередями на RabbitMQ"!

Ответить
Arunas

да да, спс., незаметил ..:)

Ответить
Алексей

Если именование Handle ещё не очень критично, так как мы его фактически нигде не вызываем, обычно это command bus, то вот с Command все сложнее. По всему коду и тестам у нас будут new Command(), чтобы понять что это за команда, надо либо полезть в use, либо делать в use алиас, либо перейти в класс команды. Всё это неудобно. Не лучше ли избавиться от папки c названием команды и перенести это в название класса, например, JoinByEmailRequestCommand, JoinByEmailRequestHandler. С одной стороны длинные названия классов, но с другой стороны в сто раз код читабельнее становится

Ответить
Дмитрий Елисеев

Можно как угодно. Становится читабельнее, но не писабельнее. А так да, если вдруг потребуется использовать две команды в одном классе (что вряд ли произойдёт), то можно использовать алиасы.

Ответить
Александр

Очень полезный урок, спасибо.

Хотелось бы узнать а как транзакции реализуются при таком подходе, предположим зарегистрировали пользователя (пользователь нужен сразу в контексте авторизации, автора и покупателя), это посути записи в три отдельных контекста.

Пользователь валидный только если он присутствует во всех трёх контекстах одновременно (по разным причинам).

Нам по сути по одному действию (регистрации) нужно кинуть три команды сразу. Две у нас выполняется, третья крашится. По условию пользователь не валидный, что делать в таком случае? В транзакцию это дело не завернешь, ну и две предыдущие нужно откатить как то ?

Ответить
Дмитрий Елисеев

Это делается через саги, подписываемые на доменные события.

Ответить
Максим

Ну погодите, как это это через саги?

Мы меняем агрегат (по которому границы транзакционности) в хэндлере, нужно это делать в транзакции в этом же хэндлере!

Единственное, там могут появиться посредники, которые это на себя возьмут, но все же или я не прав?

Ответить
Дмитрий Елисеев

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

Сага напрямую или по событию регистрации отправляет эти команды во все модули. И в случае сообщения об ошибке от любого модуля компенсирующими командами откатывает все предыдущие.

Ответить
Igor

Спасибо за уроки. Появилось пару вопросов. "Модуль" Auth это конекст из ддд терминологии? Содержимое папки Commands это Application Services или Domain Services?

Ответить
Дмитрий Елисеев

Да, разбиваем на модули по контекстам. А горизонтальное разбиение такое:

Framework:
    Web\App, Console\App
Ui:
    Http\Action, Console\Command
Application:
    Command
    Query
Domain:
    Entity
    Service
Ответить
Максим (@myks92)

Здравствуйте. Спасибо за толковый подход!) А почему структура текущего проекта не такая? Она избыточна?

И ещё один вопрос интересный. Есть мероприятия, на которые регистрируются участники. Сейчас это модуль мероприятий. Но регистрации бывают разных типов и у каждого типа своя регистрация. Я разделил все так:

Event/Registrations/Competition Event/Registrations/Battle ... другие регистрации (приобретение билетов....)

Правильно ли я сделал? Либо регистрацию вынести в отдельный модуль. А в самом модуле только использовать ID мероприятия.

Ещё, как вариант, разделить Event по типам:

Event/Competition/Registration Event/Battle/Registration

И тогда создание мероприятий будет в разных сущностях и хранится в разных таблицах. В яндекс афише тоже есть подобное. И, как мне кажется, там всё разделено. Даже приобретение билетов...

Ответить
Дмитрий Елисеев

Если делать полное разбиение по слоям, то нужно ещё и отделить слой инфраструктуры:

Application
    Command
    Query

Domain
    Entity
        class Id
        interface UserRepository
    Service
        interface PasswordHasher
    interface Flusher

Infrastructure
    Entity
        class IdType
        class DoctrineUserRepository implements UserRepository
    Service
        class CryptPasswordHasher implements PasswordHasher
    class DoctrineFlusher implements Flusher

в который вынести всю работу с Doctrine и прочие "грязные" реализации, а в Domain оставить только чистые сущности и чистые доменные интерфейсы.

Так что да, такое горизонтальное разбиение на папки часто избыточно.

Ответить
Максим (@myks92)

Видел такое разбиение у вас вhttps://github.com/ElisDN/rabbit-demo-spa. Сейчас проект и подход похожий, поэтому спросил) Понял. Спасибо за ответ!)

Ответить
Huang

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.

Ответить
Максим (@myks92)

Доброго времени суток. Спустя время появился вопрос на тему архитектуры текущей архитектуры. Появились библиотеки по контролю за архитектурой, например, https://github.com/qossmic/deptrac и сразу появилось два вопроса:

  1. Будем ли использовать подобную библиотеку в текущем скринкасте?
  2. И получается теперь настройка этой библиотеки будет сложнее, так как для простоты мы не объединяли слои в папки Application, Domain, Infrastructure и для настройки подобного контроля архитектуры потребуется каждую папку объявлять как отдельный слой и указывать для них зависимости. Может быть всё таки оправдано такое деление по слоям? Пример по слоям увидел в докладе https://youtu.be/xT25xiKqPcI?t=1801
Ответить
Дмитрий Елисеев

Может быть всё таки оправдано такое деление по слоям?

Искусственное деление на такие слои может быть оправдано внутри модуля или простого приложения. И папка инфраструктуры будет полезна, если мы явно отделяем интерфейсы в домене от реализаций в инфраструктуре.

В большом проекте простое раскладывание всех классов горизонтально на три папки проследит за обращением по слоям вниз, но не спасёт от обращения в бок, когда application-сервис блога вдруг обратится к domain-сущности магазина. А избавление от лишних связей между модулямии полезнее, чем между слоями.

И получается теперь настройка этой библиотеки будет сложнее...

Верно. Мы раскладываем классы по модулям и компонентам, а не растаскиваем на папки по типу. Отдельно горизонтально у нас вынесен в свою папку только слой Http.

У нас в папке компонента лежат вместе все его классы и интерфейсы. Например, в компоненте FeatureToggle у нас три интерфейса для использования в разных местах, класс с реализацией и Twig-расширение. И даже если внутри модуля есть отдельные папки для команд и сущностей, то всё равно классы доменных сущностей и репозиториев лежат вместе с инфраструктурными Doctrine-типами.

Будем ли использовать подобную библиотеку в текущем скринкасте?

Учитывая вышесказанное, для полноценного использования deptrac в нашем проекте в его конфигурации придётся буквально вручную описывать каждый класс и интерфейс для компонентов. И для каждого модуля вычленять все команды, репозитории и типы регулярными выражениями по суффиксам классов. Тогда будет полный контроль, но много усилий уйдёт на конфигурирование сотен классов и правил.

Но как компромисс мы можем использовать его поверхностно, объявляя в нём только папки целого компонента или модуля вроде Auth и Mailer.

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

Ответить
Дмитрий Елисеев

Если регистрация производится одинаково, то вынести в Registration c EventId.

Если на Competition и Battle по-разному регистрируются, то проще каждому сделать свою отдельную регистрацию Event/Competition/Registration и Event/Battle/Registration.

Ответить
Максим (@myks92)

Да, регистрации разные. Кроме того, есть система подсчета результатов для каждого события.

В итоге получается:
Event/Competition/Registration
Event/Competition/Result

Event/Battle/Registration
Event/Battle/Result

Вот и подумал, может это вынести в отдельные системы, как вы говорили про склад и другие...

Ответить
Артём

Дмитрий, скажите, пожалуйста, если проект у нас полностью на yii2 (advanced) : frontend, backend, api - тогда CQS оставляем в папке апи, а сущности выносим в какую-нибудь папку, например, auction? То есть структура будет такой:

  • backend
  • frontend
  • api > auth > command
  • name_project > auth > Entity

Верно размышляю?

Ответить
Дмитрий Елисеев

Да, в этом случае делаем отдельную папку auction, а в остальных папках осталяем контролеры и представления.

Ответить
Максим (@myks92)

Кстати. Дмитрий. Нашел очень хороший сервис Auth0 бесплатный тариф до 7000 АКТИВНЫХ пользователей. Отлично подходит на микросервисы. Почему не использовать бы его в своих проектах и не писать каждый раз свой Auth. Понимаю, что мы учимся и Auth может быть со своими требованиями, но не плохой сервис как по мне. Что скажете?)

Ответить
Дмитрий Елисеев

Да, сервис популярный. Но управление ролями и связывание соцсетей есть только в платном тарифе, который доходит до $1k в месяц. Если нужна двухфакторная аутентификация по SMS, то с хранением телефона на их сервере вероятно нарушается требование о хранении персональных данных в РФ. Амазоновский IP-адрес может случайно попасть под блокировку РКН. Ну и для активной работы всё равно может потребоваться кастомная интеграция. Так что нюансов тоже много.

Ответить
Максим (@myks92)

Интересно. Приоткрыли глаза) Жаль. Сервис хороший. Не так сильно его изучал, но теперь буду осторожнее с ним. Думал, что это может быть хорошим решением. Хотелось услышать ваше мнение. Как всегда много полезного! Спасибо ещё раз!

Ответить
Максим (@myks92)

Здравствуйте) Последняя тенденция модуль Auth называть Id (Identity). Например, Сбербанк ID, DeworkerPro ID. Может быть в этом проекте тоже назвать данный модуль так? Или я не так понимаю? Заодно будет ролик про то, когда меняется что Глобальное)

Ответить
Дмитрий Елисеев

Да, можно пойти по примеру OpenID и назвать Identity и вынести на поддомен id.xxx. Это дело вкуса.

Ответить
Shoyad

Здравствуйте

Спасибо, отличный материал

На основе этой архитектуры планирую сделать телеграм бот на node js (telegraf.js).

То есть человек заходить отправляет токен бота и ему поднимается телеграм бот новый инстанс

через React админку можно включать и отклать этот бот. Как лучше реализовать ?

Ответить
Дмитрий Елисеев

Да, из фронтенда на React по API и WebSocket общаться с бэкендом на Node JS вместо PHP.

Ответить
Санжар

Немного не понял про Use Case. Это же по сути сервис? Т.е с тем же успехом можно было сделать сервис хранящий бизнес-логику, или я что-то путаю?

Ответить
Дмитрий Елисеев

Про это дополнительно рассказал в статье про объекты в своём блоге.

Ответить
Зарегистрируйтесь или войдите чтобы оставить комментарий

Или войти через:

Google
GitHub
Yandex
MailRu