Прекрасный курс. Было бы интересно также разбор архитектурных паттернов, то как вы это видите. Я прошел курс Нестерука по паттернам проектирования, тоже прекрасный курс. С вашим подходом, я думаю будет круто. Ну или часто используемые паттерны разобрать для начала в рамках чистой архитектуры. Кроме cqrs, mediatr...
2:06 чем аргументируется "доведение производительности приложения до максимума"? Медиатр наоборот добавляет оверхед связанный с построением цепочки обработки реквеста. Если имеется ввиду разделение на read-write хранилища, то это из другой темы... Опять же не раскрыто зачем это нужно. Перед людьми, которые впервые слышат про CQRS и видя приведенный в видео код, встанет вопрос: "Чем это лучше CRUD-сервиса". Другие же, насмотревшись подобных уроков, пойдут распространять "Культ Карго" не понимая до конца use-case'ов при которых нужно использовать подобные паттерны. 11:00 зачем было каждое проперти из одного класса маппить вручную на другой, когда либой это все решается (можно опять обратиться к статье Богарда)? Ну и встает дискуссионный вопрос относительно необходимости интерфейса `IMapWith`, поскольку, как мне кажется это смешение инфраструктурного слоя с аппликационным и засорение DTO лишними деталями.
в 12 версии MediatR return Unit.Value; при IRequestHandler или IRequestHandler не прокатит, типы не совпадают чтобы повторять за автором устанавливайте 9 версию
@@khlopkoffengineer как любят зачастую сделать опцию, потом переименовать, убрать, передумать в новых версиях это рабочие моменты, хотя должна быть обратная совместимость и из новых версий доступны все опции старых
просидел с этим пару часов, пытаясь разобраться: "почему не работает?". в итоге пришел к этому. public async Task Handle(UpdateNoteCommand request, CancellationToken cancellationToken) { var entity = await _dbContext.Notes.FirstOrDefaultAsync(note => note.Id == request.Id, cancellationToken);
if (entity == null || entity.Id != request.UserId) { throw new NotFoundException (nameof(Note), request.Id); }
await _dbContext.SaveChangesAsync(cancellationToken); } компилятор не ругается, но допустимо ли это решение? или отсутствие возвращаемого типа мне боком выйдет? пишу вслепую осваивая правильный подход к написанию программ, поэтому зачастую не понимаю что делаю. хочу шпаргалку замутить просто, правильную и работоспособную. отсюда и вопрос
Для того чтобы код работал не обязательно откатывать до 9 версии как автор данного видео, можно откатить до 11.1.0(это максимально рабочая версия, более новые уже не работают)
Если поля у объектов называются одинаково, то маппер попытается их сам смапить и явно указывать это не нужно. Требуется только если не совпадают типы, вложенность или название. Или логика нужна какая-то. Если все вручную прописывать так, то на большом проекте с огромными объектами состариться можно)
Разве не проще профаил файлы просто прописывать и все мапы там настраивать, и никаких интерфейсов IMapWith, тогда все модельки анемичные получаются и без привязки к мапперу, а в конфигурации использовать добавление профаил классов с generic версией метода. Возможно я что то не понял
Никакой, насколько я понимаю. Вью моделью принято называть дтошки презентайшенал лэйера, т.е. то что возвращается юзеру в респонсе веб апишки или что уходит во вьюшку в mvc.
у меня есть вопрос. как работать с маппингом когда нужно из двух или более Entity-таблиц получить данные, которые должны быть замеппены к одному выходному типу к примеру: User, Person, Address, и Conact данные человека
Вполне серьезно в рамках такой объёмной серии видео.потому что бывает смотришь туториал и там порой не показывают откуда что импортится и это затрудняет освоение материала.поэтому было решение прописывать вручную, к тому же эти моменты ускорены и много времени просмотра не отнимают.конечно, в реальной жизни мы так не делаем:)
подойдет ли данный курс для создания мессенджера (чат с разделением на беседы, возможность написать конкретному пользователю из списка контактов пользователей) по аналогии? (Клиент - WPF)
удобно вкладывать класс Handler в класс команды/запроса т.к они друг без друга не имеют смысла, валидатор можно туда же. Таким образом отдельные папки не нужны и значительно меньше навигации по файлам зачем, кстати, вручную прописывать маппинги? оно же там само маппится по конвенции в целом, конечно, море копипасты с этим CQRS
насчет вкладывания одних классов в другие - это конечно от подхода зависит. но мы в целом не любим такой подход. считаем, что классы должны быть простыми и в идеале не большими. а то, что там папки - вроде это не составляет проблемы, а привносит структуру :) в общем, тут на вкус и цвет насчет маппингов. да, в Automapper можно использовать кучу соглашений (следовало упомянуть об их существовании в видео). однако чтобы их использовать нужна определенная квалификация. тут есть несколько потенциальных проблем. если возникнет ошибка и ее нужно будет исправлять то тому человеку, кому выпадет это удовольствие также нужно будет обладать квалификацией о соглашениях. а тут мы один раз их прописали и если что то сразу видно что конкретно с чем маппится. но в целом да, конвенции - это очень мощная и крутая вещь! Спасибо вам за обратную связь!!
Было бы очень интересно посмотреть, как этим люди пользуются. И как это работает своими глазами. Потому что так, вроде бы кратко. Вроде бы интересно и работает. Но непонятно
11:39 Очень странно это выглядит - автомаппер может смапить эти два класса сам без дополнительного расписывания ForMember Да и вообще вся работа с Автомаппером уж сильно усложнена для такого простого приложения. Признавайтесь - по какой книге все это написано? :-)
Я тут погуглил по интернету и у многих есть вопрос как юзать такую реализацию маппинга IMapWith Сам знаю, что при одинаковых именах маппинг идёт самостоятельно, но IMapWith использует интерфейс по умолчанию (тут было у меня некоторое не знание) Интерфейсы по умолчанию не могут вызываться, если мы работаем с Типом на класс, а не на этот интерфейс Получается чтобы использовать этот интерфейс на не достаточно унаследоваться от него, а всё равно надо переопределить метод Mapping для создания профиля
Ох, я только с маппингами разобрался, а теперь еще и медиатр и cqrs, удивительно, как я почти прошел собеседование пару лет назад не зная всего этого, знал только crud (Не попал только из-за проблем с военкоматом). Сейчас буду куда более готовым, спасибо за курс.
1) Насчет Dto и Vm... Почему их назвали по разному? В каких случаях стоит именовать их тем или иным образом? Если например NoteVm на клиентской стороне будет использоваться в одном случае как часть другой ViewModel, а в другом как отдельная ViewModel. Как правильно именовать их в этом случае? 2) Если entity подразумевает наследование, например Note - базовый класс, от которого происходят штук 12 классов наследников с разными свойствами, то как правильно поступать в этом случае с командами на Update и Create? Создавать на каждый класс наследник отдельную команду или одна команда где содержатся все возможные свойства для всех наследников?
1) У нас одни и те же данные имеют разные представления. Для того чтобы не было путаницы с именами (мы можем классы с одним и тем же именем определить в разных сборках) мы решаем их называть по разному. И у нас появляются суффиксы, как соглашение. Тут просто логическое разделение - данные и представление. Ключевой момент тут - это соглашение, принятое на проекте. Оно может быть вообще любым (обоснованно конечно и понятно членам команды). С другой стороны конечно лучше использовать популярные соглашения. 2) Если у нас есть иерархия, то мы можем просто сделать параллельную иерархию команд. Сделать общую команду и наследников. Тут нужно понимать контекст, как оно хранится в хранилище. Т.е. если мы обрабатываем каждый подтип отдельно, то и проблемы в создании нескольких классов нет
Такой вопрос: как правильно сделать маппинг, если есть классы-сущности Parent и Child, у Parent есть List Childs, и мне нужно сделать ParentViewModel таким, чтобы там был List, полученный из List от Parent. Как это лучше прописать в методе Mapping класса ParentViewModel, можно ли обойтись без Select в options вызова ForMember, и если нельзя, то как обратиться к mapper внутри этого Select (достаточно ли внедрить IMapper в ParentViewModel через конструктор, или это так не работает)?
Пару комментариев / вопросов: 1. Приятный голос и большие шрифты - то что нужно! 2. Ждем видео про DI. 3. Валидаторы появятся в проекте позже ? 4. Automapper прекрасно справляется если названия и классы полей совпадают. Зачем Вы прописывали маппинг для всех полей ? Для наглядности ? 5. Где-то можно будет скачать исходный код решения ? Поставил лайк за Ваше видео. Спасибо Вам!
1. Спасибо!!!! 3. Да, валидацию мы добавим немного позже 4. Вы правы. в Automapper можно использовать соглашения (следовало упомянуть об их существовании в видео). Но чтобы их использовать нужна определенная квалификация. тут в целом могут возникать потенциальные проблемы. например если возникнет ошибка и ее нужно будет исправлять то тому человеку, кому выпадет это удовольствие также нужно будет обладать квалификацией о соглашениях. а тут мы один раз их прописали и если что то сразу видно что конкретно с чем маппится. но в целом да, соглашения Automapper'а - это очень мощная и крутая вещь. 5. Да, конечно, можно будет взять полный код всего того, что происходит на экране
спасибо большое за видео ! ) mediatR - это что-то типа на событийную шину похоже, или rxjs; т.е. если все сущности будут общаться через mediatR, то инжектирование классов может не понадобиться ?
Что-то не могу понять: По заявленному ранее, в философии CQRS, Есть запросы (не меняют состояние системы) и команды (меняют состояние системы). Описанные модули UpdateCommand и DeleteCommand - очевидно меняют состояние системы, но наследуются от IRequest. Как жить дальше? 🙂
что-то пошло не так похоже, хорошее замечание, спасибо) п.с. но суть не поменялась - да, мы реализовали не с тем именем интерфейс, но концепция в целом верная, наши команды делают то для чего созданы. возможно стоит провести работу над ошибками в отдельном видео)
@@romangromov7606 Есть слова Query и Request. Посмотрите определение и станет понятно. Все Command/Query - это запросы (request). А обработчики запросов, вне зависимости меняют ли они данные или нет, уже Request (запрос) Handler (обработчик). Мб станет понятнее, если правильно уловил суть вопроса вашего.
Доброго времени суток. Такой вопрос. Можно ли добавить референс на проект апликэшн в клиентскую часть (фронт) на пример в блэзор проект, что бы использовать команды как запросы, а вью модели как ответы? И как вообще архитектура cqrc наклыдывается на REST? Или может наоборот следует создать отдельно запросы и ответы в домэйне и уже с них мапиться на команды и вьюмодели? В общем смысл вопроса сводится к одному как не копипастить классы запросов-ответов в клиентской части?
Относительно AddAsync из доки: Этот метод является асинхронным, чтобы разрешить специальные генераторы значений, такие как microsoft.EntityFrameworkCore.Metadata.SqlServerValueGenerationStrategy.SequenceHiLo, для асинхронного доступа к базе данных. Во всех остальных случаях следует использовать несинхронный метод.
Это получается, каждый запрос с апи у нас будет соответствовать одному отдельному классу? Т.е в более "классическом" виде, к примеру, у нас был бы какой-нибудь один контроллер Notes, и в нем было бы множество апи-методов GetOne, GetAll и т.п., каждый из которых = отдельному запросу А здесь: отдельный запрос = отдельному классу. Так? Если так, то не будет ли издишнее раздувание количества классов?
не совсем. здесь тоже самое что и в "классическом" виде есть методы Get, GetAll и т.д., но логика каждого запроса вынесена отдельно. количество классов зависит от количества видов манипуляции с данными (создание, изменение и т.д.)
@@PlatinumTechTalks Да, спасибо. У вас классная архитектура. Но без знания именно Асп было тяжко, раз по 5 пересматривал. У вас приятный голос, спасибо за то, что вы, в целом, есть
разница концептуальная. дело подхода и вкуса. мы считаем, что база данных - это просто хранилище, единственная цель которого - хранить данные, никакой логики в идеале там быть не должно. (как вариант сценария: что если будет происходить миграция с одной базы на другую - будет ли она нормально поддерживать создание гуидов? не факт)
Я могу ошибаться, поправьте меня если что, но использовать CancellationToken в в таких местах, где ты пытаешься что-то сохранить и записать, особенно в случае с базой данных, не очень хорошая идея, вы потом при отладке могут возникнуть проблемы, с непониманием того, какого же черта ничего не записывается в базу.
Предположим, что есть идея логировать в базе все запросы от пользователей, в том числе и запросы на получение информации (queries). Можно ли говорить о том, что это уже не queries, а команды, так как происходит некое изменение данных (добавление некой записи в таблицу логирования) ?
Я бы рассматривал в данном случае базу данных как абстактное хранилище. CQRS вообще по хорошему реализуется так, что запись происходит в одну базу данных, а чтение из другой. Это позволяет иметь несколько баз для чтения вследствии такого масштабирования. Так вот, в вашем вопросе вы размещаете хранилище приложения и хранилище логов в одной базе данных. Т.е. это все равно queries, т.к. они не меняют данных приложения. Можно ли все писать в одну базу данных? Возможно что и да, зависит от вас, но я бы по карйней мере держал все в разных базах, пусть и в одном сервере базы данных.
Без преувеличения - у Вас одни из лучших IT-видео на рутубе!
Согласен
Отлично!
И шрифт большой .
Спасибо
Спасибо вам!
п.с. вот теперь уже сообразили про шрифты =D
Прекрасный курс. Было бы интересно также разбор архитектурных паттернов, то как вы это видите. Я прошел курс Нестерука по паттернам проектирования, тоже прекрасный курс. С вашим подходом, я думаю будет круто. Ну или часто используемые паттерны разобрать для начала в рамках чистой архитектуры. Кроме cqrs, mediatr...
2:06 чем аргументируется "доведение производительности приложения до максимума"? Медиатр наоборот добавляет оверхед связанный с построением цепочки обработки реквеста. Если имеется ввиду разделение на read-write хранилища, то это из другой темы...
Опять же не раскрыто зачем это нужно. Перед людьми, которые впервые слышат про CQRS и видя приведенный в видео код, встанет вопрос: "Чем это лучше CRUD-сервиса". Другие же, насмотревшись подобных уроков, пойдут распространять "Культ Карго" не понимая до конца use-case'ов при которых нужно использовать подобные паттерны.
11:00 зачем было каждое проперти из одного класса маппить вручную на другой, когда либой это все решается (можно опять обратиться к статье Богарда)? Ну и встает дискуссионный вопрос относительно необходимости интерфейса `IMapWith`, поскольку, как мне кажется это смешение инфраструктурного слоя с аппликационным и засорение DTO лишними деталями.
Ничего не сказала рыбка, лишь хвостом по воде плеснула...))
в 12 версии MediatR return Unit.Value; при IRequestHandler или IRequestHandler не прокатит, типы не совпадают
чтобы повторять за автором устанавливайте 9 версию
Спасибо, кстати, а с чем это связанно конкретно?
@@khlopkoffengineer как любят зачастую сделать опцию, потом переименовать, убрать, передумать в новых версиях это рабочие моменты, хотя должна быть обратная совместимость и из новых версий доступны все опции старых
просидел с этим пару часов, пытаясь разобраться: "почему не работает?".
в итоге пришел к этому.
public async Task Handle(UpdateNoteCommand request, CancellationToken cancellationToken)
{
var entity = await _dbContext.Notes.FirstOrDefaultAsync(note => note.Id == request.Id, cancellationToken);
if (entity == null || entity.Id != request.UserId)
{
throw new NotFoundException (nameof(Note), request.Id);
}
entity.Details = request.Details;
entity.Title = request.Title;
entity.EditDate = DateTime.Now;
await _dbContext.SaveChangesAsync(cancellationToken);
}
компилятор не ругается, но допустимо ли это решение? или отсутствие возвращаемого типа мне боком выйдет? пишу вслепую осваивая правильный подход к написанию программ, поэтому зачастую не понимаю что делаю. хочу шпаргалку замутить просто, правильную и работоспособную. отсюда и вопрос
Для того чтобы код работал не обязательно откатывать до 9 версии как автор данного видео, можно откатить до 11.1.0(это максимально рабочая версия, более новые уже не работают)
ty
мы для получения 1 сущности создали кучу папок и кучу обработчиков. Неужели нельзя просто вытащить -> замапить -> получить?
thx
А как это вы вернули Task в UpdateNoteCommandHandler? Там же void?
Если поля у объектов называются одинаково, то маппер попытается их сам смапить и явно указывать это не нужно. Требуется только если не совпадают типы, вложенность или название. Или логика нужна какая-то.
Если все вручную прописывать так, то на большом проекте с огромными объектами состариться можно)
Разве не проще профаил файлы просто прописывать и все мапы там настраивать, и никаких интерфейсов IMapWith, тогда все модельки анемичные получаются и без привязки к мапперу, а в конфигурации использовать добавление профаил классов с generic версией метода. Возможно я что то не понял
Какая разница между ViewModel и Dto?
Никакой, насколько я понимаю. Вью моделью принято называть дтошки презентайшенал лэйера, т.е. то что возвращается юзеру в респонсе веб апишки или что уходит во вьюшку в mvc.
а как из избавиться от Boilerplate code в командах ?
По коду непонятно зачем вы настраиваете ForMemeber для AutoMapper если все поля имеют одинаковый тип и названия.
а как поставить знак не равно как в видео?
В видео особенный шрифт, поэтому знаки так выглядят, обычно пишут вот так !=
Это шрифт от JetBrains, созданный специально для разработчиков: www.jetbrains.com/ru-ru/lp/mono/
у меня есть вопрос. как работать с маппингом когда нужно из двух или более Entity-таблиц получить данные, которые должны быть замеппены к одному выходному типу к примеру: User, Person, Address, и Conact данные человека
мапать вручную =)
using`и прописывать вручную, да вы серьезно?
Вполне серьезно в рамках такой объёмной серии видео.потому что бывает смотришь туториал и там порой не показывают откуда что импортится и это затрудняет освоение материала.поэтому было решение прописывать вручную, к тому же эти моменты ускорены и много времени просмотра не отнимают.конечно, в реальной жизни мы так не делаем:)
подойдет ли данный курс для создания мессенджера (чат с разделением на беседы, возможность написать конкретному пользователю из списка контактов пользователей) по аналогии? (Клиент - WPF)
удобно вкладывать класс Handler в класс команды/запроса т.к они друг без друга не имеют смысла, валидатор можно туда же. Таким образом отдельные папки не нужны и значительно меньше навигации по файлам
зачем, кстати, вручную прописывать маппинги? оно же там само маппится по конвенции
в целом, конечно, море копипасты с этим CQRS
насчет вкладывания одних классов в другие - это конечно от подхода зависит. но мы в целом не любим такой подход. считаем, что классы должны быть простыми и в идеале не большими. а то, что там папки - вроде это не составляет проблемы, а привносит структуру :) в общем, тут на вкус и цвет
насчет маппингов. да, в Automapper можно использовать кучу соглашений (следовало упомянуть об их существовании в видео). однако чтобы их использовать нужна определенная квалификация. тут есть несколько потенциальных проблем. если возникнет ошибка и ее нужно будет исправлять то тому человеку, кому выпадет это удовольствие также нужно будет обладать квалификацией о соглашениях. а тут мы один раз их прописали и если что то сразу видно что конкретно с чем маппится.
но в целом да, конвенции - это очень мощная и крутая вещь!
Спасибо вам за обратную связь!!
харош не забрасывай только, лучшее что я видел, спасибо тебе от моих глаз и ушей
Да, а я думал, что здесь научат))
В итоге сижу на десяти страницах и мониторю каждую технологию)
Было бы очень интересно посмотреть, как этим люди пользуются. И как это работает своими глазами.
Потому что так, вроде бы кратко. Вроде бы интересно и работает. Но непонятно
11:39 Очень странно это выглядит - автомаппер может смапить эти два класса сам без дополнительного расписывания ForMember
Да и вообще вся работа с Автомаппером уж сильно усложнена для такого простого приложения.
Признавайтесь - по какой книге все это написано? :-)
скорее всего такая книга еще не написана =)
Я тут погуглил по интернету и у многих есть вопрос как юзать такую реализацию маппинга IMapWith
Сам знаю, что при одинаковых именах маппинг идёт самостоятельно, но IMapWith использует интерфейс по умолчанию (тут было у меня некоторое не знание)
Интерфейсы по умолчанию не могут вызываться, если мы работаем с Типом на класс, а не на этот интерфейс
Получается чтобы использовать этот интерфейс на не достаточно унаследоваться от него, а всё равно надо переопределить метод Mapping для создания профиля
Ох, я только с маппингами разобрался, а теперь еще и медиатр и cqrs, удивительно, как я почти прошел собеседование пару лет назад не зная всего этого, знал только crud (Не попал только из-за проблем с военкоматом).
Сейчас буду куда более готовым, спасибо за курс.
Спасибо! Удачи вам!
Ну как успехи? Осилил? Я сейчас на этом этапе :)
@@winstochurgle9133 ну как осилил?)
@@hiryu70 Разумеется :)
@@winstochurgle9133 где и над чем работаешь?)
1) Насчет Dto и Vm... Почему их назвали по разному? В каких случаях стоит именовать их тем или иным образом? Если например NoteVm на клиентской стороне будет использоваться в одном случае как часть другой ViewModel, а в другом как отдельная ViewModel. Как правильно именовать их в этом случае?
2) Если entity подразумевает наследование, например Note - базовый класс, от которого происходят штук 12 классов наследников с разными свойствами, то как правильно поступать в этом случае с командами на Update и Create? Создавать на каждый класс наследник отдельную команду или одна команда где содержатся все возможные свойства для всех наследников?
1) У нас одни и те же данные имеют разные представления. Для того чтобы не было путаницы с именами (мы можем классы с одним и тем же именем определить в разных сборках) мы решаем их называть по разному. И у нас появляются суффиксы, как соглашение. Тут просто логическое разделение - данные и представление.
Ключевой момент тут - это соглашение, принятое на проекте. Оно может быть вообще любым (обоснованно конечно и понятно членам команды). С другой стороны конечно лучше использовать популярные соглашения.
2) Если у нас есть иерархия, то мы можем просто сделать параллельную иерархию команд. Сделать общую команду и наследников.
Тут нужно понимать контекст, как оно хранится в хранилище. Т.е. если мы обрабатываем каждый подтип отдельно, то и проблемы в создании нескольких классов нет
Такой вопрос: как правильно сделать маппинг, если есть классы-сущности Parent и Child, у Parent есть List Childs, и мне нужно сделать ParentViewModel таким, чтобы там был List, полученный из List от Parent. Как это лучше прописать в методе Mapping класса ParentViewModel, можно ли обойтись без Select в options вызова ForMember, и если нельзя, то как обратиться к mapper внутри этого Select (достаточно ли внедрить IMapper в ParentViewModel через конструктор, или это так не работает)?
Писать код, ради того что бы писать код? Хоть какой то промежуточный результат глянуть можно?
Привет. У меня id прописаны не guid, a int. Как заменить эту строку Id = Guid.NewGuid() на такую же, только для инта? 5:40
может в Notes.Domain в классе Notes добавить под Id атрибут [DatabaseGenerated(DatabaseGeneratedOption.Identity)], но я точно не уверен...
Пару комментариев / вопросов:
1. Приятный голос и большие шрифты - то что нужно!
2. Ждем видео про DI.
3. Валидаторы появятся в проекте позже ?
4. Automapper прекрасно справляется если названия и классы полей совпадают. Зачем Вы прописывали маппинг для всех полей ? Для наглядности ?
5. Где-то можно будет скачать исходный код решения ?
Поставил лайк за Ваше видео. Спасибо Вам!
1. Спасибо!!!!
3. Да, валидацию мы добавим немного позже
4. Вы правы. в Automapper можно использовать соглашения (следовало упомянуть об их существовании в видео). Но чтобы их использовать нужна определенная квалификация. тут в целом могут возникать потенциальные проблемы. например если возникнет ошибка и ее нужно будет исправлять то тому человеку, кому выпадет это удовольствие также нужно будет обладать квалификацией о соглашениях. а тут мы один раз их прописали и если что то сразу видно что конкретно с чем маппится. но в целом да, соглашения Automapper'а - это очень мощная и крутая вещь.
5. Да, конечно, можно будет взять полный код всего того, что происходит на экране
5. под каждым видео теперь добавлены ссылки на код из видео
а если сущностей очень много, это для каждой сущности это всё прописывать ??!!
тутор закончился очень внезапно)
там же продолжение в других видео плейлиста есть)
спасибо большое за видео ! )
mediatR - это что-то типа на событийную шину похоже, или rxjs; т.е. если все сущности будут общаться через mediatR, то инжектирование классов может не понадобиться ?
А как настроить такие же стрелочки и операторы сравнения в райдере?
www.jetbrains.com/lp/mono/
А можно узнать что делает .ProjectTo(_mapper.ConfigurationProvider)?
Библиотека MediatR не реализует паттерн Посредник.
Что-то не могу понять: По заявленному ранее, в философии CQRS, Есть запросы (не меняют состояние системы) и команды (меняют состояние системы).
Описанные модули UpdateCommand и DeleteCommand - очевидно меняют состояние системы, но наследуются от IRequest.
Как жить дальше? 🙂
что-то пошло не так похоже, хорошее замечание, спасибо)
п.с. но суть не поменялась - да, мы реализовали не с тем именем интерфейс, но концепция в целом верная, наши команды делают то для чего созданы.
возможно стоит провести работу над ошибками в отдельном видео)
@@PlatinumTechTalks Тоже это заметил, можете подсказать интерфейс для command, ICommand и ICommand нет.
Возможно я что-то не понял, прошу разъяснения.
Все правильно. В библиотеке Mediatr при создании Command или Query используется IRequest.
@@alewarten то, что используется - вижу. Я указал на противоречие философии и практики.
@@romangromov7606 Есть слова Query и Request. Посмотрите определение и станет понятно. Все Command/Query - это запросы (request).
А обработчики запросов, вне зависимости меняют ли они данные или нет, уже Request (запрос) Handler (обработчик).
Мб станет понятнее, если правильно уловил суть вопроса вашего.
Доброго времени суток. Такой вопрос. Можно ли добавить референс на проект апликэшн в клиентскую часть (фронт) на пример в блэзор проект, что бы использовать команды как запросы, а вью модели как ответы? И как вообще архитектура cqrc наклыдывается на REST? Или может наоборот следует создать отдельно запросы и ответы в домэйне и уже с них мапиться на команды и вьюмодели? В общем смысл вопроса сводится к одному как не копипастить классы запросов-ответов в клиентской части?
Видео супер! А как поменять отображение != в visual studio?))
Относительно AddAsync из доки:
Этот метод является асинхронным, чтобы разрешить специальные генераторы значений, такие как microsoft.EntityFrameworkCore.Metadata.SqlServerValueGenerationStrategy.SequenceHiLo, для асинхронного доступа к базе данных. Во всех остальных случаях следует использовать несинхронный метод.
хороший пример, мне понравился CQRS
Огромное спасибо! Наконец то понятно!
Микросервисы будут?
нет, микросервисов не будет...
@@PlatinumTechTalks На, мужик, микросервисы...
Это получается, каждый запрос с апи у нас будет соответствовать одному отдельному классу? Т.е в более "классическом" виде, к примеру, у нас был бы какой-нибудь один контроллер Notes, и в нем было бы множество апи-методов GetOne, GetAll и т.п., каждый из которых = отдельному запросу А здесь: отдельный запрос = отдельному классу. Так? Если так, то не будет ли издишнее раздувание количества классов?
не совсем. здесь тоже самое что и в "классическом" виде есть методы Get, GetAll и т.д., но логика каждого запроса вынесена отдельно. количество классов зависит от количества видов манипуляции с данными (создание, изменение и т.д.)
А есть ссылка на гитхаб проекта, что бы смотреть на код и слушать комментарий, а не смотреть, как молча набирают код
Да, в описании к каждому видео есть ссылка на код из него
Если имена классов совпадают в автомаппере можно не указывать это.
да, всё так, всё так
мне одному кажется что это слишко?
Делал все до этого видео, только на .Net 6 и библиотеку все выше по версии. Словил ошибку пришлось удалить все.
Харош
Харош
Хотел изучить именно АСП кор, он тут будет? Или он уже где-то был, и я пропустил?..
очень поздний ответ, но все же.. вы нашли асп кор в итоге?
@@PlatinumTechTalks Да, спасибо. У вас классная архитектура. Но без знания именно Асп было тяжко, раз по 5 пересматривал. У вас приятный голос, спасибо за то, что вы, в целом, есть
Спасибо
Automapper 🤦🏻
плохо, да?
А есть разница между тем где создается новый Guid, в бд или в самом приложении, если да то как сделать лучше?
разница концептуальная. дело подхода и вкуса. мы считаем, что база данных - это просто хранилище, единственная цель которого - хранить данные, никакой логики в идеале там быть не должно.
(как вариант сценария: что если будет происходить миграция с одной базы на другую - будет ли она нормально поддерживать создание гуидов? не факт)
@@PlatinumTechTalks Спасибо за ответ.
@@PlatinumTechTalks если в ней есть guid'ы, то скорей всего поддержку гуидов она поддерживает
Как расшифровывается "opt"?
Сокращение от options
@@PlatinumTechTalks Понял, спасибо
Спасибо
и Вам!!
Я могу ошибаться, поправьте меня если что, но использовать CancellationToken в в таких местах, где ты пытаешься что-то сохранить и записать, особенно в случае с базой данных, не очень хорошая идея, вы потом при отладке могут возникнуть проблемы, с непониманием того, какого же черта ничего не записывается в базу.
Commands - это Update, Delete, Create. Очень похожи для всех сущностей. Можно сделать абстракцию и просто дженериком принимать тип сущности?
Предположим, что есть идея логировать в базе все запросы от пользователей, в том числе и запросы на получение информации (queries). Можно ли говорить о том, что это уже не queries, а команды, так как происходит некое изменение данных (добавление некой записи в таблицу логирования) ?
Я бы рассматривал в данном случае базу данных как абстактное хранилище. CQRS вообще по хорошему реализуется так, что запись происходит в одну базу данных, а чтение из другой. Это позволяет иметь несколько баз для чтения вследствии такого масштабирования. Так вот, в вашем вопросе вы размещаете хранилище приложения и хранилище логов в одной базе данных. Т.е. это все равно queries, т.к. они не меняют данных приложения.
Можно ли все писать в одну базу данных? Возможно что и да, зависит от вас, но я бы по карйней мере держал все в разных базах, пусть и в одном сервере базы данных.
@@faelkre спасибо за ответ
@@faelkre спасибо за Ваше мнение и ответ