Counterpoint.OnDemandProjection behaviour (counterpoint v0.1.0)

Copy Markdown View Source

Read-side projection that folds events into state on every call.

Unlike a persistent projection, no state is stored between calls — events are re-read and re-folded each time run/3 is invoked. This is simple and always consistent, at the cost of re-reading events on every request.

The query/1 callback may use Query.limit/2 and Query.reverse/1, making it suitable for "last N events" patterns (e.g. checking whether a book is currently borrowed by fetching only the most recent event).

use Counterpoint.OnDemandProjection injects @behaviour Counterpoint.OnDemandProjection.

Example

defmodule MyApp.Views.OrderSummary do
  use Counterpoint.OnDemandProjection
  alias Counterpoint.Query
  alias MyApp.Events.{OrderPlaced, OrderCancelled}

  defstruct [:order_id, :status]

  @impl Counterpoint.OnDemandProjection
  def query(order_id) do
    Query.new()
    |> Query.add_item(types: [OrderPlaced, OrderCancelled], tags: ["order_id:#{order_id}"])
  end

  @impl Counterpoint.OnDemandProjection
  def init, do: %__MODULE__{}

  @impl Counterpoint.OnDemandProjection
  def apply(state, %Counterpoint.Envelope{data: %OrderPlaced{order_id: id}}),
    do: %{state | order_id: id, status: :placed}

  def apply(state, %Counterpoint.Envelope{data: %OrderCancelled{}}),
    do: %{state | status: :cancelled}
end

Counterpoint.OnDemandProjection.run(MyApp.Views.OrderSummary, :my_store, "order-1")

Summary

Callbacks

Fold a single envelope into the current state.

Return the initial (empty) state before any events are applied.

Build the query used to fetch events for this projection.

Functions

Execute the projection for args against the given store.

Callbacks

apply(state, envelope)

@callback apply(state :: term(), envelope :: Counterpoint.Envelope.t()) :: term()

Fold a single envelope into the current state.

init()

@callback init() :: term()

Return the initial (empty) state before any events are applied.

query(args)

@callback query(args :: term()) :: Counterpoint.Query.t()

Build the query used to fetch events for this projection.

Functions

run(module, store_name, args \\ nil)

Execute the projection for args against the given store.

Fetches events using module.query(args), then folds them with module.apply/2 starting from module.init().