Skip to content

Sequenced Read Models and improved Read Model Before Hooks.

Pre-release
Pre-release
Compare
Choose a tag to compare
@javiertoledo javiertoledo released this 27 Aug 14:47
· 955 commits to main since this release

This release introduces two features:

Sequenced Read Models

This release introduces the ability to model read models as time sequenced (Or time series). This feature is useful to model a series of data objects that are indexed by the time they were logged. This might sound similar to events, but sequenced read models are also mutable, so they can change over time without losing their place in the sequence. An example use case could be a chat application, where all messages are identified by a specific channel ID and a timestamp of when they were sent, but individual messages can be edited or deleted.

Booster provides a special decorator to tag a specific property as a sequence key for a read model:

export class MessageReadModel {
  public constructor(
    readonly id: UUID, // A channel ID
    @sequencedBy readonly timestamp: string,
    readonly contents: string
  )

  @Projects(Message, 'id')
  public static projectMessage(entity: Message, currentReadModel: MessageReadModel): ProjectionResult<MessageReadModel> {
    return new MessageReadModel(entity.id, entity.timestamp, entity.contents)
  }
}

Adding a sequence key to a read model changes the behavior of its query, which now accepts the sequence key as an optional parameter:

query MessageReadModel(id: ID!, timestamp: string): [MessageReadModel]

In this query, when only the id is provided, you get an array of all the messages in the channel ordered by timestamp in ascending order (from older to newer). When you also provide a specific timestam, you still get an array, but it will only contain the specific message sent at that time.

You can read more about sequenced read models in the documentation.

Improved Read Model Before Hooks

Read Model Before Hooks allow you to set up one or more functions that are called before a read model request is processed, having the opportunity of altering or canceling the request. A common use case for read model before hooks is managing complex access restrictions policies that depend on the business logic, like allowing a user to access objects they own but not objects they should not have access to.

Starting from this release, before hooks will receive the entire request object received from GraphQL, and not only the filters, opening the door to more complex rewrites of specific queries. Before hook functions will receive and return ReadModelRequestEnvelope objects which have the following signature:

interface ReadModelRequestEnvelope<TReadModel> {
  currentUser?: UserEnvelope // The current authenticated user
  requestID: UUID // An ID assigned to this request
  key?: { // If present, contains the id and sequenceKey that identify a specific read model 
    id: UUID
    sequenceKey?: SequenceKey
  }
  className: string // The read model class name
  filters: ReadModelRequestProperties<TReadModel> // Filters set in the GraphQL query
  limit?: number // Query limit if set
  afterCursor?: unknown // For paginated requests, id to start reading from
}