Readmodel
The Readmodel interface in comby defines the contract for managing queryable representations of domain state. It combines two key responsibilities: event handling and state restoration, enabling the read model to remain up-to-date and consistent with the event-sourced domain.r validating the command and emitting events as a result of the command being executed.
Readmodel interface
In comby an Readmodel
is defined as an interface
as follows:
type Readmodel interface {
// EventHandler provides methods for handling domain events and updating the read model.
EventHandler
// StateRestorer provides methods for restoring the read model's state from a sequence of events.
StateRestorer
}
As an extension of the EventHandler interface, the Readmodel includes methods for processing domain events. This allows the read model to react to events emitted by aggregates, updating its state accordingly to ensure it reflects the latest changes in the system.
The Readmodel also implements the StateRestorer interface, which provides methods for reconstructing the read model's state from a historical sequence of events. This functionality is essential for initializing or recovering the read model in event-sourced architectures, ensuring that it can be derived entirely from the event store.
By combining these interfaces, the Readmodel serves as a foundational component for building consistent, queryable views of domain data in comby.
Base Readmodel
The BaseReadmodel is a foundational implementation of the Readmodel interface in comby, designed to manage the creation, restoration, and maintenance of queryable projections in event-sourced architectures. It provides mechanisms for handling domain events, restoring state from an event repository, and tracking metrics related to event processing.
type BaseReadmodel struct {
*BaseIdentifier // Embeds a base identifier for readmodel identification.
domainEvthandlers []DomainEventHandlerEntry // Registered domain event handlers.
EventRepository *EventRepository // Repository for retrieving events.
CatchedEvents []Event // Events captured during state restoration.
RestorationState string // Current state of the restoration process.
lastEventTimestamp int64 // Timestamp of the last processed event.
ctxCancelFunc context.CancelFunc // Cancel function for the restoration context.
metrics *metrics // Metrics for tracking readmodel performance.
}
At its core, the BaseReadmodel embeds a BaseIdentifier to uniquely associate the read model with a domain and name. It maintains a list of registered domain event handlers (domainEvthandlers), allowing it to dynamically handle specific domain events as they occur. The read model also integrates with an EventRepository, which serves as the source for historical events during state restoration or rehydration.
Key features of the BaseReadmodel include:
State Restoration: The RestorationState and CatchedEvents fields track the progress of restoring the read model’s state from events stored in the repository. Event Handling: The GetDomainEventHandlers method dynamically retrieves the list of handler functions for a specified domain event, ensuring efficient processing. The GetDomainEvents method provides a comprehensive list of unique domain events that the read model can handle. Metrics and Context Management: The metrics field tracks performance-related data for the read model, while the ctxCancelFunc allows graceful cancellation of long-running restoration processes. The NewBaseReadmodel function initializes the BaseReadmodel, setting up its dependencies, including the event repository and performance metrics tracking. This ensures that the read model is ready to handle events and restore state effectively.
To extend functionality, the addDomainEventHandler method allows developers to register handler functions for specific domain events. Each handler is associated with metadata, such as the event's type path and name, and stored in the domainEvthandlers list. This design ensures flexibility in event handling, supporting both specific event types and catch-all handlers (e.g., AllEvents).
Custom User-defined Readmodel
type CustomOrderReadmodel struct {
*comby.BaseReadmodel
// any additional fields
}
func NewCustomReadmodel(EventRepository *comby.EventRepository) *CustomOrderReadmodel {
rm := &CustomOrderReadmodel{}
rm.BaseReadmodel = comby.NewBaseReadmodel(EventRepository)
rm.Domain = "Order"
rm.Name = "CustomOrderReadmodel"
// register domain event handlers
comby.AddDomainEventHandler(rm, rm.TenantCreatedEvent)
comby.AddDomainEventHandler(rm, rm.TenantRemovedEvent)
// ...
return rm
}
func (rm *CustomOrderReadmodel) RestoreState(ctx context.Context, restoreFromZero bool) (comby.RestorationDoneCh, error) {
// any additional reset logic
return rm.BaseReadmodel.RestoreState(ctx, restoreFromZero)
}
The CustomOrderReadmodel is a user-defined read model in the "Order" domain, extending the functionality of the BaseReadmodel in comby. It is designed to maintain a queryable representation of order-related data, optimized for quick access through tenant and order UUID mappings.
This read model leverages two concurrent maps (sync.Map) to efficiently store and retrieve data:
mapByTenantUuid: Maps tenant UUIDs to their associated orders. mapByOrderUuid: Maps order UUIDs to their respective details. The NewCustomReadmodel function initializes an instance of the CustomOrderReadmodel. During initialization, it sets the domain and name for identification and registers a set of domain event handlers to process events such as:
- TenantCreatedEvent
- TenantRemovedEvent
These handlers enable the read model to react to domain events, updating its state to reflect the latest changes in the system.
The RestoreState method is overridden to reset the internal maps (mapByTenantUuid and mapByOrderUuid) before invoking the base restoration process. This ensures a clean slate for restoring the read model’s state from historical events stored in the event repository. The method returns a RestorationDoneCh, signaling when the restoration process is complete.
By combining the extensibility of the BaseReadmodel with domain-specific logic and efficient data structures, the CustomOrderReadmodel provides a scalable and performant solution for querying