Генерация событий и чтение моделей - PullRequest
27 голосов
/ 31 октября 2010

Предполагается, что проблема домена переполнения стека и следующее определение событий:

UserRegistered(UserId, Name, Email)
UserNameChanged(UserId, Name)
QuestionAsked(UserId, QuestionId, Title, Question)

Предполагается следующее состояние хранилища событий (в порядке появления):

1) UserRegistered(1, "John", "john@gmail.com")
2) UserNameChanged(1, "SuperJohn")
3) UserNameChanged(1, "John007")
4) QuestionAsked(1, 1, "Help!", "Please!")

Предполагается, чтоследующая денормализованная модель чтения для списка вопросов (для первой страницы SO):

QuestionItem(UserId, QuestionId, QuestionTitle, Question, UserName)

И следующий обработчик событий (который создает денормализованную модель чтения):

public class QuestionEventsHandler
{
    public void Handle(QuestionAsked question)
    {
        var item = new QuestionItem(
            question.UserId, 
            question.QuestionId, 
            question.Title, 
            question.Question, 
            ??? /* how should i get name of the user? */);
        ...
    }
}

Мой вопрос как я могу найти имя пользователя , который задал вопрос?Или более распространенный: как мне обрабатывать события, если моя денормализованная модель чтения требует дополнительных данных, которых нет в конкретном событии?

Я исследовал существующие образцы CQRS, включая SimpleSQRS ГрегаМолодой и Фохин образец Марка Нийхофа.Но мне кажется, что они оперируют только данными, включенными в события.

Ответы [ 3 ]

24 голосов
/ 06 февраля 2012

Лично я думаю, что нет ничего плохого в поиске имени пользователя из обработчика событий. Но если вы находитесь в состоянии, когда вы не можете запросить имя у модели чтения пользователя, я бы добавил дополнительный обработчик событий в QuestionEventsHandler для обработки события UserRegistered.

Таким образом, QuestionEventsHandler может поддерживать свой собственный репозиторий имен пользователей (вам не нужно будет хранить электронную почту пользователей). Затем обработчик QuestionAsked может запросить имя пользователя непосредственно из своего собственного хранилища (как сказал Ринат Абдуллин, хранилище дешево!).

Кроме того, поскольку ваша модель чтения QuestionItem содержит имя пользователя, вам необходимо обработать событие UserNameChanged в QuestionEventsHandler, чтобы обеспечить актуальность поля имени в QuestionItem.

Мне кажется, что это меньше усилий, чем «обогащение событий», и имеет преимущество , а не построение зависимостей от других частей системы и их моделей чтения.

3 голосов
/ 03 ноября 2010

Просто обогатите событие всей необходимой информацией.

Подход Грега, насколько я помню, - обогатите событие при его создании и сохранении / публикации таким образом.

0 голосов
/ 12 января 2012

Извлечение событий из EventStore.

Помните - Ваши модели для чтения должны иметь доступ только для чтения к EventStore. Читайте модели одноразовые. Это просто кэшированные представления. Вы должны иметь возможность в любое время удалить / истечь свои модели чтения и автоматически перестроить ваши модели чтения из хранилища событий. Итак, ваши ReadModelBuilders уже должны иметь возможность запрашивать прошлые события.

public class QuestionEventsHandler
{
    public void Handle(QuestionAsked question)
    {
        // Get Name of User
        var nameChangedEvent = eventRepository.GetLastEventByAggregateId<UserNameChanged>(question.UserId);

        var item = new QuestionItem(
            question.UserId, 
            question.QuestionId, 
            question.Title, 
            question.Question, 

            nameChangedEvent.Name
    }
}

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

Я столкнулся с точно таким же сценарием ... где мне нужно было больше данных, чем было доступно в одном событии. Это особенно верно для событий типа Create, которые требуют заполнения новой модели ReadModel начальным состоянием.

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

Дополнительные данные в событиях. Вы действительно не хотите разбрасывать события, используя все дополнительные данные, необходимые для просмотра. Это действительно повредит вам, когда ваш домен изменится, и вам нужно перенести события. События домена имеют конкретные цели - они представляют изменения состояния. Не просматривать данные.

Надеюсь, это поможет -

Ryan

...