Event Repository
The EventRepository
in comby is a higher-level abstraction that integrates with an EventStore
to manage events and its domain events. It provides methods to interact with concrete implemented event storage, including adding, retrieving, listing, and deleting events. Internally, the repository also leverages the DomainEventProvider
to map raw event data to domain-specific event representations, enabling seamless integration in event-driven systems.
INFO
The DomainEventProvider
is an internal component used by the facade to enrich raw event data with domain-specific context, ensuring that events are ready for consumption before being returned to the user.
Integration with Facade
The EventRepository
serves as a intermediate mediator between the application logic and the storage layer, simplifying interactions with the EventStore
. Additionally, the AggregateRepository
is using the EventRepository
to manage aggregate related event operations, ensuring that the events are correctly stored and retrieved.
Users can utilize any compatible EventStore
implementation (e.g., in-memory or PostgreSQL) by passing it to the repository during the initialization of the facade. The repository abstracts the underlying storage mechanism, enabling users to seamlessly switch between different storage backends without altering the application logic. All that’s required is to instantiate the desired EventStore
and provide it to the facade, which handles the rest.
import "github.com/gradientzero/comby/v2"
import "github.com/gradientzero/comby/v2/store"
// create in-memory event store
eventStoreMemory := comby.NewEventStoreMemory()
// or SQLite event store
eventStoreSQLite := store.NewEventStoreSQLite("/path/to/event-store.db")
// create facade with event store
fc, _ := comby.NewFacade(
comby.FacadeWithEventStore(eventStoreMemory),
// or
comby.FacadeWithEventStore(eventStoreSQLite),
)
// get event repository
eventRepo := fc.GetEventRepository()
Core Responsibilities
- Add Events: This operation adds new events and passes them to the underlying
EventStore
for persistence. Internally, when adding events, the repository ensures that no events with identical versions are added for the same aggregate, maintaining consistency and preventing version conflicts. - Retrieve Events: Retrieves individual events by their unique identifiers, optionally enriching them with domain-specific data (loading domain event runtime model representation).
- List Events: Supports paginated and filtered retrieval of multiple events based on criteria such as tenant, domain, or timestamps.
- Delete Events: Removes events by their unique identifiers. This operation should be used with caution and used only when necessary (while developing is fine).
- Unique Listings: The repository can generate lists of unique event attributes (e.g. aggregateUuids or domains) for advanced querying or reporting.
- Integration with DomainEventProvider: For every event, the
DomainEventProvider
enriches the raw event with domain-specific context, ensuring the events are ready for consumption by aggregates or read models. - Abstraction of EventStore: The repository abstracts the underlying
EventStore
implementation, allowing users to switch between different storage backends (e.g., in-memory, PostgreSQL) without affecting the application logic.
Key Methods
The EventRepository
provides a set of methods for managing events, each with specific options to control the behavior of the operation. The repository employs option-based patterns for configuring method calls, ensuring flexibility:
Add Event
Adds a new event by wrapping it in an EventRepositoryAddOption
and delegating to the EventStore
's Create method.
AddEvent(ctx context.Context, opts ...EventRepositoryAddOption) error
Returns an error if the operation fails.
Options | Description |
---|---|
EventRepositoryAddOptionWithEvent(evt Event) | sets the event to be added. |
Get Event
Retrieves a specific event by its UUID, enriching it with domain-specific context using the DomainEventProvider
.
GetEvent(ctx context.Context, opts ...EventRepositoryGetOption) (Event, error)
Returns the event instance and an error if the operation fails.
Options | Description |
---|---|
EventRepositoryGetOptionWithEventUuid(eventUuid string) | retrieve the specific event enriched with domain event |
List Events
Lists events based on filters such as tenant UUID, aggregate UUID, domain, and creation timestamps. Pagination options (e.g., offset, limit) and sorting options (e.g., ascending or descending) are also supported.
ListEvents(ctx context.Context, opts ...EventRepositoryListOption) ([]Event, int64, error)
Returns a list of events, the total amount of found events, and an error if the operation fails.
Options | Description |
---|---|
EventRepositoryListOptionWithTenantUuid(tenantUuid string) | filter by tenant UUID |
EventRepositoryListOptionWithAggregateUuid(aggregateUuid string) | filter by aggregate UUID |
EventRepositoryListOptionWithDomains(domain ...string) | filter by domains |
EventRepositoryListOptionBefore(unixTimestamp int64) | filter events created before the specified timestamp |
EventRepositoryListOptionAfter(unixTimestamp int64) | filter events created after the specified timestamp |
EventRepositoryListOptionOffset(offset int64) | set the offset for pagination |
EventRepositoryListOptionLimit(limit int64) | set the limit for pagination |
EventRepositoryListOptionOrderBy(orderBy string) | set the order by field for sorting |
EventRepositoryListOptionAscending(ascending bool) | set the sorting order (ascending or descending) |
Unique List Events
Generates a unique list of event attributes (e.g., aggregate UUIDs) based on the specified database field. Returns the list of unique attributes and the total count of unique attributes.
UniqueList(ctx context.Context, opts ...EventRepositoryUniqueListOption) ([]string, int64, error)
Options | Description |
---|---|
EventRepositoryUniqueListOptionWithTenantUuid(tenantUuid string) | filters unique events by tenant unique identifier. |
EventRepositoryUniqueListOptionWithDomain(domain string) | filters unique events by domain. |
EventRepositoryUniqueListOptionWithDbField(field string) | sets the database field to uniquely identify events. |
EventRepositoryUniqueListOptionOffset(offset int64) | sets the offset for pagination. |
EventRepositoryUniqueListOptionLimit(limit int64) | sets the limit for pagination. |
EventRepositoryUniqueListOptionAscending(ascending bool) | sets the sorting order (ascending or descending). |
Delete Event
Deletes an event by its unique UUID, ensuring the operation propagates through the underlying store.
DeleteEvent(ctx context.Context, opts ...EventRepositoryDeleteOption) error
Options | Description |
---|---|
EventRepositoryDeleteOptionWithEventUuid(eventUuid string) | deletes the event by its unique identifier. |
Appendix: EventRepository from Scratch
The EventRepository
can be created independently, requiring only the EventStore
and DomainEventProvider
to be passed to it. This is particularly useful for testing purposes. In production environments, however, the EventRepository
is typically always accessed via the facade. The facade takes care of registering domain events and creating the EventRepository
with the correct dependencies. However, for testing purposes, the EventRepository
can be created manually as shown below.
import "github.com/gradientzero/comby/v2"
import "github.com/gradientzero/comby/v2/store"
import "github.com/gradientzero/comby/v2/domain/tenant/aggregate"
// create in-memory event store
eventStoreMemory := comby.NewEventStoreMemory()
domainEventProvider := comby.NewDomainEventProvider()
// manually register domain events
domainEvt := &aggregate.TenantCreatedEvent{
TenantUuid: "", // not relevant
Name: "", // not relevant
}
domainEventProvider.RegisterDomainEvent("Tenant", domainEvt, true)
// The provider is now capable of deserializing a domain
// event "TenantCreatedEvent" from a byte array. This ensures
// that the event can be correctly reconstructed from its
// serialized form and processed accordingly.
eventRepo := comby.NewEventRepository(eventStoreMemory, domainEventProvider)
// When an `Event` with the domain event `TenantCreatedEvent` is loaded from the
// `EventStore`, the EventRepository can fully deserialize the event, including its
// domain-specific data. Otherwise, only the event itself can be reconstructed, without the
// domain-specific details.