CQRS и возможная последовательность
Я был добавлен в проект в разработке. Это приложение ASP.Net MVC 5, использующее Mediatr и шаблон CQRS (с базой данных только для чтения и базой данных только для записи - в конечном итоге согласованной). В приложении есть административная часть, в которой много операций CRUD, и мы сталкиваемся с проблемами. Например, скажем, есть контроллер виджетов:
public class WidgetController : Controller
{
private readonly IMediator _mediator;
public WidgetController(IMediator mediator)
{
_mediator = mediator;
}
// GET: Widget
public ActionResult Index()
{
// Reads from the read-only database and may not have synced
// This call is not guaranteed to have the newly added or edited widget (and usually doesn't)
var allWidgets = _mediator.Send(new GetAllWidgets());
return View(allWidgets);
}
[HttpPost]
public ActionResult Create(Widget widget)
{
try
{
// This call contains the database logic to write into the write only database
_mediator.Send(new CreateWidget(widget));
return RedirectToAction("Index");
}
catch
{
return View();
}
}
[HttpPost]
public ActionResult Edit(Widget updatedWidget)
{
try
{
// Writes to the write-only database
_mediator.Send(new UpdateWidget(updatedWidget));
return RedirectToAction("Index");
}
catch
{
return View();
}
}
}
В действиях "Создать и редактировать" виджет либо создается, либо редактируется, и эти изменения вносятся в базу данных только для записи. Затем следует немедленное перенаправление к действию индекса, которое читает из базы данных только для чтения. Изменения, как правило, не синхронизируются из базы данных только для записи к моменту завершения этого вызова и представления представления. Чтобы обойти эту проблему, мы использовали Redis для кэширования вновь созданного или обновленного объекта, а затем, если список, полученный в действии Index, не содержит нового или отредактированного объекта, мы извлекаем его из кэша. Это действительно очень неправильно.
Поскольку никто из нас в проекте никогда не участвовал в проекте CQRS, мы не знаем, как исправить эту проблему. Такое ощущение, что мы действительно что-то упустили с этим шаблоном.
Итак, я думаю, что я спрашиваю, это... есть ли лучшая практика для обработки сценариев такого типа? Есть лучший способ сделать это?
3 ответа
В CQRS постоянство записи и постоянство чтения разделены (логически, временно и даже физически), и то, что вы испытываете, является нормальным, вы должны принять это и не рассматривать это как фундаментальную проблему. Вместо этого вы должны изменить свой клиент, чтобы компенсировать это. Например, вы должны сделать вызов для обновления сущностей в фоновом режиме (например, AJAX) вместо перенаправления клиента на страницу редактирования. Это одно из самых простых решений. Другие являются более сложными, например, использование корреляционных или каузальных идентификаторов, последних измененных временных отметок, ожидание на уровне приложений или презентации модели чтения для обработки событий и т. Д.
Итак, я думаю, что я спрашиваю, это... есть ли лучшая практика для обработки сценариев такого типа? Есть лучший способ сделать это?
Многие из частей, которые вам нужны, уже на месте.
Одним из подходов к CQRS является то, что записи выполняются в реальном представлении модели предметной области, но чтения происходят в кэшированных представлениях.
HTTP довольно хорошо разбирается в кешировании. В частности, HTTP-кеши понимают, что небезопасные операции делают недействительными кэшированные представления. Таким образом, любой ответ без ошибок на запрос POST приведет к аннулированию кэшированных данных.
У вас даже есть код состояния для работы с возможной последовательностью; 202 Принято объявляет посредникам, что запрос не является ошибкой (делает недействительными кэши), но "обработка не завершена". Как и в случае ответа 200, полезная нагрузка представляет собой "представление статуса действия".
Таким образом, вы можете отправить клиенту 202 Принятый со ссылкой на монитор состояния, в котором есть информация, необходимая для того, чтобы узнать, обновлена ли модель чтения. Например, модель записи может знать, какую "версию" объекта вы ожидаете, или сколько событий (если вы выполняете поиск событий), или идентификатор корреляции для самой публикации.
Кодируйте эти метаданные в целевой ресурс для монитора состояния, и клиент может опрашивать монитор состояния, пока он не покажет, что модель чтения была обновлена.
Существует даже понимание слабых валидаторов, которые могут быть возвращены операцией модификации, чтобы указать версию ресурса, без необходимости знать особенности представления, которое потребуется для сильного валидатора.
Но... я признаю, что не нахожу, что ассортимент деталей производит удовлетворительное целое. Такое ощущение, что отсутствует кусок - что-то, что позволяет нам возвращать валидатор с представлением статуса действия, которое помогает клиенту сделать соответствующий условный запрос на чтение представления.
Очень быстрый поиск того, как справиться с возможной последовательностью. В моих проектах мы полагаемся главным образом на уведомления о веб-сокетах (например, SignalR), но иногда "поддельные ответы" являются достаточно хорошим подходом.
Я хотел бы знать, почему вы используете CQRS, особенно для операций CRUD. Кажется, что если вы переоцениваете свой дизайн. Асинхронность сопряжена с множеством проблем