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

Анализ Use Cases взаимодействия с модулем аутетнификации. Выделение команд Command и запросов Query. Принцип CQS разделения операций.

Скрытый контент
Комментарии (32)
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
Ответить
Максим

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

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

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 оставить только чистые сущности и чистые доменные интерфейсы.

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

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

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

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

Если регистрация производится одинаково, то вынести в 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? То есть структура будет такой:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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