Skip to content

Projection

In an application framework that follows the Event Sourcing (EV) and Command Query Responsibility Segregation (CQRS) patterns, there is a strict separation between the write and read sides. The write side handles commands and business logic, ensuring that changes are applied to the system's state, while the read side focuses on serving data, often in the form of projections or read models. This separation allows for greater flexibility and scalability, as each side can be optimized independently.

Projections, which are objects fullfilling the EventHandler interface, are built by applying domain events to form a materialized view of the system’s state. Using Golang's generics and interfaces, it becomes easier to define these projections by reusing code to transform domain aggregates into read models. This flexibility allows developers to efficiently create tailored read models while maintaining the strict separation of concerns within the CQRS architecture.

Projections, or Readmodels, are typically used to serve data in response to Queries in a CQRS-based architecture. The read side focuses on efficient data retrieval. Projections are built by processing these events to form a queryable representation of the system’s state. When a Query is executed, it typically interacts with one or more projections to return the required data in a fast and efficient manner.

Simple Creation of Projections from Aggregates

In many cases, existing aggregates can be directly used to build projections without impacting the aggregates themselves or the write side of the system. This allows for a straightforward way to represent the current state of the domain in a readmodel, leveraging the data that is already available in the system.

By using this approach, you can create efficient projections while maintaining the strict separation between the read and write sides. In the next section, we will explore how custom projections can be built, offering more flexibility to tailor readmodels to specific query requirements.

go
projRm := projection.NewProjectionAggregate(
	eventRepository,
	aggregate.NewAggregate,
)
if err := comby.RegisterEventHandler(fc, projRm); err != nil {
	return err
}

// next use projection somewhere (ignore error handling for now)
key := "MyKey"
val, _ := projRm.GetModel(key)
go
// register (predefined) query handler for the readmodel
projQh := projection.NewQueryHandler(projRm)
if err := comby.RegisterQueryHandler(fc, projQh); err != nil {
	return err
}