Aprovisionamiento de eventos y Generación de Modelos leídos


Asumiendo el problema del dominio de desbordamiento de pila y la siguiente definición de eventos:

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

Asumiendo el siguiente estado del almacén de eventos (en el orden de aparición):

1) UserRegistered(1, "John", "[email protected]")
2) UserNameChanged(1, "SuperJohn")
3) UserNameChanged(1, "John007")
4) QuestionAsked(1, 1, "Help!", "Please!")

Asumiendo el siguiente modelo de lectura desnormalizada para la lista de preguntas (para la primera página de SO):

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

Y el siguiente controlador de eventos (que construye el modelo de lectura denormalizado):

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? */);
        ...
    }
}

Mi pregunta es ¿cómo puedo encontrar el nombre del usuario que hizo una pregunta? O más común: ¿cómo debo manejar los eventos si mi modelo de lectura desnormalizada requiere datos adicionales que no existen en el evento en particular?

He examinado muestras existentes de CQRS incluyendo SimpleSQRS de Greg Young y Fohjin muestra de Mark Nijhof. Pero me parece que se operan solo con datos que se incluyen en los eventos.

Author: Dmitry Schetnikovich, 2010-10-31

3 answers

Simplemente enriquece el evento con toda la información necesaria.

El enfoque de Greg, según recuerdo, enriquece el evento mientras lo creas y lo almacenas/publicas de esta manera.

 3
Author: Rinat Abdullin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-11-03 08:29:55

Personalmente creo que no hay nada malo en buscar el nombre del usuario desde el controlador de eventos. Pero si estás en una posición en la que no puedes consultar el nombre del modelo de lectura del Usuario, entonces presentaría un controlador de eventos adicional a QuestionEventsHandler, para manejar el evento registrado por el usuario.

De esa manera el QuestionEventsHandler podría mantener su propio repositorio de nombres de usuario (no necesitaría almacenar el correo electrónico de los usuarios). El controlador QuestionArked puede entonces consultar el nombre directo de su propio repositorio (como Rinat Abdullin dijo que el almacenamiento es barato!).

Además de que tu modelo QuestionItem read contiene el nombre del usuario, también necesitarás manejar el evento UserNameChanged dentro del controlador QuestionEventsHandler para asegurarte de que el campo name dentro del QuestionItem esté actualizado.

Para mí esto parece menos esfuerzo que 'enriquecer los eventos' y tiene el beneficio de no construir dependencias en otras partes del sistema y sus modelos leídos.

 22
Author: Chris Moutray,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-02-06 14:46:05

Extrae eventos del EventStore.

Recuerde: Sus modelos de lectura ya deben tener acceso de solo lectura al EventStore. Los modelos leídos son desechables. Son simplemente vistas en caché. Debería poder eliminar / caducar sus Modelos de lectura en cualquier momento y reconstruir automáticamente sus ReadModels desde EventStore. Por lo tanto, sus ReadModelBuilders ya deben poder consultar eventos pasados.

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
    }
}

También tenga en cuenta que el repositorio EventStore no necesita ser el verdadero EventStore, aunque ciertamente podría serlo. La ventaja de los sistemas distribuidos es que puede replicar fácilmente el EventStore, si lo necesita, más cerca de sus ReadModels.

Me encontré exactamente con el mismo escenario... donde necesitaba más datos de los que estaban disponibles en un solo evento. Esto es especialmente cierto para los eventos de tipo Create que requieren rellenar un nuevo ReadModel con el estado inicial.

De los Modelos Leídos: Se puede extraer de otros Modelos Leídos. Pero realmente no recomiendo esto, ya que introduciría una gran bola de lodo de dependencia donde las vistas dependen de las vistas dependen de las vistas.

Datos adicionales en Eventos: Realmente no quieres inflar tus eventos con todos los datos adicionales que necesitarás para las vistas. Realmente le hará daño cuando su dominio cambie y necesite migrar eventos. Los eventos de dominio tienen propósitos específicos: representan cambios de estado. No ver datos.

Espero que esto ayude -

Ryan

 -1
Author: Ryan D. Hatch,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-01-11 20:12:48