Skip to content

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.

go
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.

go
AddEvent(ctx context.Context, opts ...EventRepositoryAddOption) error

Returns an error if the operation fails.

OptionsDescription
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.

go
GetEvent(ctx context.Context, opts ...EventRepositoryGetOption) (Event, error)

Returns the event instance and an error if the operation fails.

OptionsDescription
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.

go
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.

OptionsDescription
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.

go
UniqueList(ctx context.Context, opts ...EventRepositoryUniqueListOption) ([]string, int64, error)
OptionsDescription
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.

go
DeleteEvent(ctx context.Context, opts ...EventRepositoryDeleteOption) error
OptionsDescription
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.

go
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.