Evento Framework
  • Introduction
  • Architecture Overview
    • Distributed Systems & Microservices
    • Recative Manifesto & Reactive Principles
    • State-of-the-art Patterns
      • DDD (Domain Driven Design)
      • CQRS (Command Query Responsability Separation)
      • Event Sourcing
      • Messaging
      • Saga
  • GETTING STARTED
    • Quick Start
    • TodoList - RECQ Tutorial
      • Problem Description and Requirement Gathering
      • RECQ Payload Design
      • RECQ Components Design
      • Set up your Development Environment
      • RECQ Payload Evento Implementation
        • Domain Commands
        • Domain Events
        • Views
        • Queries
      • RECQ Components Evento Implementation with Spring Data
        • TodoListAggregate
        • TodoList Model with Spring Data
        • TodoListProjector
        • TodoListProjection
        • TodoList Invoker
      • Expose the RECQ architecture with Spring Web
      • Test Your App
    • Extend TodoList - Handle Complexity Tutorial
      • Unique identifier generation
      • Extends behaviors with Observer and Services
      • Cross Domain Consistency with Sagas
      • Handle Real time data updates with MQTT and Save-Notify Pattern
  • RECQ Patterns
    • RECQ Patterns
    • RECQ System Pattern
      • Component
      • Message Gateway
      • System State Store
    • RECQ Communication Pattern
      • Component to Component
      • Component to System State Store
      • System State Store to Component
    • RECQ Component Pattern
      • Aggregate
      • Projector
      • Projection
      • Service
      • Invoker
      • Saga
      • Observer
  • Evento Framework
    • Evento Framework Introcution
    • Payload and Messages
      • Command
        • Domain Command
        • Service Command
      • Event
        • Domain and Service Event
      • Query and View
    • @Component
      • @Aggregate
        • Aggregate State
        • @AggregateCommandHandler
        • @EventSourcingHandler
      • @Projector
        • Projector @EventHandler
      • @Projection
        • @QueryHandler
      • @Service
        • @CommandHandler
      • @Invoker
      • @Saga
        • SagaState
        • @SagaEventHandler
      • @Observer
    • Dead Event Queues
    • EventoBundle
      • EventoServerMessageBusConfiguration
      • ConsumerStateStore
        • InMemoryConsumerStateStore
        • PostgresConsumerStateStore
        • MysqlConsumerStateStore
      • Context
      • TracingAgend and @Track
        • SentryTracingAgent
      • Autoscaling Protocol
        • ThreadCountAutoscalingProtocol
      • Injector and @Component
  • Evento Server
    • Evento Server Introduction
    • SetUp Evento Server
      • Advanced Options
      • Evento Event Store Modes: APES and CPES
    • Evento Server Cluster
    • Bundle Deploy Script
  • EVENTO GUI
    • Explore RECQ Systems Visually
    • GUI Auth
    • Payload Catalog
    • Component Catalog
    • Bundle Catalog
    • Cluster Status (Experimental)
    • Flows
      • Performance Evaluation
    • Application Graph
    • System State Store
  • Evento CLI
    • Evento CLI Introduction
    • Update Version
    • Publish
Powered by GitBook
On this page
  1. Evento Framework
  2. @Component

@Projector

Materializing Domain State

In the realm of CQRS (Command Query Responsibility Segregation), projectors play a crucial role in bridging the gap between the write model (domain events) and the read model (queryable data). This chapter dives into the concept of projectors within Evento and explores how they are used to materialize the current state of your domain from domain events.

Understanding Projectors

The @Projector annotation identifies a class as a projector within your Evento application. Projectors are responsible for handling domain events published as a result of command execution and updating a corresponding projection state stored in a database. Essentially, they translate domain events into a format suitable for querying and retrieval.

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Component
public @interface Projector {
    /**
     * Returns the version of the Projector.
     *
     * @return the version of the Projector
     */
    int version();
}

Here's a breakdown of the @Projector annotation:

  • @Retention(RetentionPolicy.RUNTIME): This ensures that the annotation information is retained at runtime, allowing Evento to access it during application execution.

  • @Target(ElementType.TYPE): This specifies that the annotation can only be applied to class declarations.

  • @Component: This indicates that the annotated class is a component within the Evento framework.

The @Projector annotation also includes a version parameter:

  • version (int): This parameter specifies the version of the projector. This versioning mechanism becomes crucial when significant changes are made to the projection logic. A new version allows Evento to potentially recompute the projection state based on the revised logic.

How Projectors Work

Projectors rely on the @EventHandler annotation to define methods that react to specific domain events. These event handler methods typically follow this pattern:

  1. Event Reception: The event handler method receives a domain event object as input.

  2. Projection Update Logic: Based on the received event, the handler method updates the corresponding projection state in the database. This might involve creating a new entry, modifying existing data, or potentially deleting data from the projection.

  3. Optional Additional Logic: In some scenarios, projectors might perform additional tasks like logging or sending notifications after updating the projection state.

Example: Projector in Action

import com.evento.common.messaging.gateway.QueryGateway;
import com.evento.common.modeling.annotations.component.Projector;
import com.evento.common.modeling.annotations.handler.EventHandler;
import com.evento.common.modeling.messaging.message.application.EventMessage;
import com.evento.demo.api.event.DemoCreatedEvent;
import com.evento.demo.api.event.DemoDeletedEvent;
import com.evento.demo.api.event.DemoUpdatedEvent;
import com.evento.demo.api.utils.Utils;
import com.evento.demo.query.domain.Demo;
import com.evento.demo.query.domain.DemoRepository;

import java.time.Instant;

@Projector(version = 3)
public class DemoProjector {

    private final DemoRepository demoRepository;

    public DemoProjector(DemoRepository demoRepository) {
       this.demoRepository = demoRepository;
    }

    @EventHandler
    void on(DemoCreatedEvent event, QueryGateway queryGateway, EventMessage<?> eventMessage) {
       Utils.logMethodFlow(this, "on", event, "BEGIN");
       var now = Instant.now();
       demoRepository.save(new Demo(event.getDemoId(), event.getName(),
             event.getValue(), now, now, null));
       Utils.logMethodFlow(this, "on", event, "END");
    }

    @EventHandler
    void on(DemoUpdatedEvent event) {
       Utils.logMethodFlow(this, "on", event, "BEGIN");
       var now = Instant.now();
       demoRepository.findById(event.getDemoId()).ifPresent(d -> {
          d.setName(event.getName());
          d.setValue(event.getValue());
          d.setUpdatedAt(Instant.now());
          demoRepository.save(d);
       });
       Utils.logMethodFlow(this, "on", event, "END");

    }

    @EventHandler
    void on(DemoDeletedEvent event) {
       Utils.logMethodFlow(this, "on", event, "BEGIN");
       demoRepository.findById(event.getDemoId()).ifPresent(d -> {
          d.setDeletedAt(Instant.now());
          demoRepository.save(d);
       });
       Utils.logMethodFlow(this, "on", event, "END");

    }
}

The provided code example showcases a DemoProjector class:

  • The class is annotated with @Projector(version = 3), indicating its version.

  • It has a constructor that injects a DemoRepository dependency.

  • Three @EventHandler methods handle DemoCreatedEvent, DemoUpdatedEvent, and DemoDeletedEvent respectively.

  • Each event handler method updates the Demo projection object in the database based on the information contained in the corresponding domain event.

Key Points:

  • Projectors provide a mechanism to materialize the current state of your domain from the stream of domain events.

  • The versioning mechanism associated with projectors allows for flexibility when evolving the projection logic.

  • Projectors work hand-in-hand with query repositories to enable efficient retrieval of domain data for querying purposes.

In the next chapter, we'll explore how projectors integrate with the consumer state store within Evento to manage the overall event processing flow.

Previous@EventSourcingHandlerNextProjector @EventHandler

Last updated 1 year ago