Привет,
Спасибо, что написали эту статью о CQRS. Я пытаюсь понять, как выглядит реализация, и просматривал примеры. Образец кода репозитория, представленный в примере, очень похож на то, как обычно можно писать репо, за исключением соглашения об именах. Например, я думаю, что репозиторий, приведенный ниже, такой же, как ProductsCommandHandler
и, вероятно, одно отличие будет заключаться в том, что здесь нет GetProduct
который мы обычно добавляем. Не могли бы вы объяснить, чего я здесь не понимаю?
public class ProductRepository {
void AddNewProduct(Product newProduct) {
...
}
void RateProduct(int productId, int userId, int rating) {
var product = repository.Find(productId);
if (product != null)
{
product.RateProduct(userId, rating);
repository.Save(product);
}
}
}
⚠ Не редактируйте этот раздел.
@martinmthomas Спасибо за вопрос! Мы рассмотрим и предоставим обновления по мере необходимости.
@MikeWasson какие-нибудь мысли здесь?
AB № 160217 - Благодарим за сообщение - эта проблема находится на рассмотрении
@martinmthomas На самом деле обработчик команд не является репозиторием. Он использует репозиторий.
Обработчик команд предназначен для «обработки фактических команд, если они подходят». В этом примере класс ProductsCommandHandler
получает в своем конструкторе IRepository<Product>
.
Допустим, пользователь собирается поставить товар 5555 на 4 звезды.
{"product":"5555","stars":4}
RateProduct
и заполняет продукт 5555, звездочки 4. В этом примере он также заполняет, кто оценивает.CommandHandler
в своем контроллере (вероятно, путем внедрения зависимостей) и просто помещаете туда команду: h.Handle (c); где c - команда RateProduct.Итак, 3 варианта: а) получить обработчик, б) получить очередь, в) получить шину и позволить ей решить отправить команду обработчику или очереди.
Какой бы подход вы ни выбрали ... главное отличие «классического репозитория» в том, что вы НИКОГДА не вызываете сам репозиторий в контроллере. Контроллер не «знает, как управлять сущностями» (классический репозиторий), но знает, «как управлять НАМЕРЕНИЯМИ ДЕЙСТВИЙ с сущностями» (обработчик команд).
Затем это обработчик команд, который, как следует из его названия, "обрабатывает команду", которая пришла откуда-то (веб-контроллер html, контроллер API, командная строка, что угодно), которая была отправлена как намерение, и это CommandHandler (который принадлежит стороне записи), которая решает, что делать с этой командой.
Например, CommandHandler может использовать репо для получения продукта, установки состояния и сохранения (как в примере), но он также может записывать в журнал событий, запускать элементы для обновления сторон чтения, запускать внешние соединители или что-то еще.
Пользовательский интерфейс, основанный на задачах, позволяющий оценить продукт и его контроллер как AGNOSTIC относительно того, «где» размещена или хранится система звездочек / рейтингов. Представьте, что вы разрабатываете систему с нуля, а класс Product уже содержит метод RateProduct (), как в примере. Хорошо.
Но ... что, если у вас есть устаревшая система со "старым" подходом к Продукту. В «модели» (например, Business Mind) нет «рейтинга» продукта. Вместо этого маркетолог хочет «добавить» рейтинг к существующему продукту, но вся компания соглашается, что это «внешняя вещь». Вы бы использовали репозиторий продукта? Или, может быть, другое вспомогательное хранилище, чтобы вы не «трогали» уже работающие и полностью протестированные продукты и репозиторий продуктов?
Если вы пользуетесь командами, то это не жалко пишущему контроллеру. Интернет, API и интерфейс командной строки просто отправят «команду» обработчику команд (либо напрямую, через очередь, либо через командную шину, которая, в свою очередь, направит ее в обработчик или в очередь), и они забудут. Затем обработчик команд решит, что делать с «RateProductCommand» в централизованном и небольшом месте вашего исходного кода, это отделяет способ обработки от кода приложения, тем самым обеспечивая возможность обслуживания.
Затем обработчик решит, можно ли использовать ProductRepository или какой-либо другой подход для хранения «рейтинга продукта».
Итак, чтобы ответить:
CommandHandler => не имеет ничего общего с объектами. Он обрабатывает «намерения пользователя» (которые в конечном итоге могут заключаться в изменении сущностей; поэтому наиболее вероятно, что обработчик команд использует репозиторий).
Репозиторий => фактическое хранилище для определенного объекта.
Надеюсь на помощь.
Хави.
Как упоминалось в @xmontero , ProductsCommandHandler и ProductRepository имеют только отношение «имеет». ProductsCommandHandler действует как интерфейс между ProductApi / Controller и ProductRepository. Эта реализация помогает хранить команды отдельно от запросов. В традиционном подходе мы напрямую используем репозиторий из контроллера, сохраняя выполнение команд и запросов вместе, поэтому нет необходимости в каком-либо другом интерфейсе между ними.
Вы применили шаблон проектирования CQRS в своем приложении, если вы сохраняете команды отдельно от запросов. Это приведет вас к получению всех преимуществ CQRS, упомянутых в статьях.
Самый полезный комментарий
@martinmthomas На самом деле обработчик команд не является репозиторием. Он использует репозиторий.
Обработчик команд предназначен для «обработки фактических команд, если они подходят». В этом примере класс
ProductsCommandHandler
получает в своем конструктореIRepository<Product>
.Допустим, пользователь собирается поставить товар 5555 на 4 звезды.
{"product":"5555","stars":4}
RateProduct
и заполняет продукт 5555, звездочки 4. В этом примере он также заполняет, кто оценивает.CommandHandler
в своем контроллере (вероятно, путем внедрения зависимостей) и просто помещаете туда команду: h.Handle (c); где c - команда RateProduct.Итак, 3 варианта: а) получить обработчик, б) получить очередь, в) получить шину и позволить ей решить отправить команду обработчику или очереди.
Какой бы подход вы ни выбрали ... главное отличие «классического репозитория» в том, что вы НИКОГДА не вызываете сам репозиторий в контроллере. Контроллер не «знает, как управлять сущностями» (классический репозиторий), но знает, «как управлять НАМЕРЕНИЯМИ ДЕЙСТВИЙ с сущностями» (обработчик команд).
Затем это обработчик команд, который, как следует из его названия, "обрабатывает команду", которая пришла откуда-то (веб-контроллер html, контроллер API, командная строка, что угодно), которая была отправлена как намерение, и это CommandHandler (который принадлежит стороне записи), которая решает, что делать с этой командой.
Например, CommandHandler может использовать репо для получения продукта, установки состояния и сохранения (как в примере), но он также может записывать в журнал событий, запускать элементы для обновления сторон чтения, запускать внешние соединители или что-то еще.
Пользовательский интерфейс, основанный на задачах, позволяющий оценить продукт и его контроллер как AGNOSTIC относительно того, «где» размещена или хранится система звездочек / рейтингов. Представьте, что вы разрабатываете систему с нуля, а класс Product уже содержит метод RateProduct (), как в примере. Хорошо.
Но ... что, если у вас есть устаревшая система со "старым" подходом к Продукту. В «модели» (например, Business Mind) нет «рейтинга» продукта. Вместо этого маркетолог хочет «добавить» рейтинг к существующему продукту, но вся компания соглашается, что это «внешняя вещь». Вы бы использовали репозиторий продукта? Или, может быть, другое вспомогательное хранилище, чтобы вы не «трогали» уже работающие и полностью протестированные продукты и репозиторий продуктов?
Если вы пользуетесь командами, то это не жалко пишущему контроллеру. Интернет, API и интерфейс командной строки просто отправят «команду» обработчику команд (либо напрямую, через очередь, либо через командную шину, которая, в свою очередь, направит ее в обработчик или в очередь), и они забудут. Затем обработчик команд решит, что делать с «RateProductCommand» в централизованном и небольшом месте вашего исходного кода, это отделяет способ обработки от кода приложения, тем самым обеспечивая возможность обслуживания.
Затем обработчик решит, можно ли использовать ProductRepository или какой-либо другой подход для хранения «рейтинга продукта».
Итак, чтобы ответить:
CommandHandler => не имеет ничего общего с объектами. Он обрабатывает «намерения пользователя» (которые в конечном итоге могут заключаться в изменении сущностей; поэтому наиболее вероятно, что обработчик команд использует репозиторий).
Репозиторий => фактическое хранилище для определенного объекта.
Надеюсь на помощь.
Хави.