Я был добавлен в проект в разработке. Это приложение 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, мы не знаем, как исправить эту проблему. Такое ощущение, что мы действительно что-то упустили с этим шаблоном.

Итак, я думаю, что я спрашиваю, это ... есть ли лучшая практика для обработки сценариев такого типа? Есть лучший способ сделать это?

0
Matt M 20 Авг 2018 в 23:33

3 ответа

Лучший ответ

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

2
Constantin Galbenu 21 Авг 2018 в 07:05

Итак, я думаю, что я спрашиваю, это ... есть ли лучшая практика для обработки сценариев такого типа? Есть лучший способ сделать это?

Многие из частей, которые вам нужны, уже на месте.

Один из способов думать о CQRS - записи происходят в реальном представлении модели предметной области, а чтения - в кэшированных представлениях.

HTTP довольно хорошо понимает кэширование. В частности, HTTP-кэши понимают, что небезопасные операции делают недействительными кэшированные представления. Таким образом, любой ответ без ошибок на запрос POST приведет к аннулированию кэшированных данных.

У вас даже есть код состояния для работы с возможной последовательностью; 202 Принято объявляет посредникам, что запрос не является ошибкой (аннулирование кэши) но "обработка не завершена". Как и в случае 200 ответа, полезная нагрузка представляет собой «представление Статус акции ".

Таким образом, вы можете отправить клиенту 202 Принятый со ссылкой на монитор состояния, в котором есть информация, необходимая для того, чтобы узнать, обновлена ли модель чтения. Например, модель записи может знать, какую «версию» объекта вы ожидаете, или сколько событий (если вы используете источник событий), или идентификатор корреляции для самого сообщения.
Кодируйте эти метаданные в целевой ресурс для монитора состояния, и клиент может опрашивать монитор состояния, пока он не покажет, что модель чтения была обновлена.

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

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

1
VoiceOfUnreason 21 Авг 2018 в 03:23

Очень быстрый поиск способов обработки возможной согласованности . В моих проектах мы опираемся в основном на уведомления о веб-сокетах (например, SignalR), но иногда «поддельные ответы» достаточно хороший подход.

Я хотел бы знать, почему вы используете CQRS, особенно для операций CRUD. Кажется, что если вы переоцениваете свой дизайн. Асинхронность сопряжена с множеством проблем, с которыми приходится сталкиваться

1
wilver 20 Авг 2018 в 21:33
51938139