Skip to content

State Views Overview

State Views are WebAssembly components that implement the Evolve pattern, materializing queryable state representations from event streams. They provide efficient, consistent access to derived state without the overhead of recomputing from events on every query.

State Views implement a reduce function that processes events to build state:

State' = evolve(State, Event)

Each State View:

  • Subscribes to specific event types via selectors
  • Maintains state by processing events in order
  • Can be queried at any revision for point-in-time state
  • Supports parameterized queries for filtering

State Views are implemented as WebAssembly (WASM) components that:

  • Run in a sandboxed environment for security
  • Are version-controlled for safe upgrades
  • Can be written in any language that compiles to WASM
  • Follow the Component Model for interoperability

State Views are published via multipart/form-data POST request with two parts:

  • metadata: JSON containing the state view configuration
  • wasm: The compiled WebAssembly component binary
Terminal window
curl -X POST http://localhost:3000/api/v1/db/my-events/state-views \
-F 'metadata={
"state_view_name": "customer-profile",
"description": "Aggregates customer order history",
"content_type": "application/json",
"event_selector": {
"type": "or",
"selectors": [
{"type": "equals", "attr": {"event_type": "CustomerCreated"}},
{"type": "equals", "attr": {"event_type": "OrderPlaced"}},
{"type": "equals", "attr": {"event_type": "OrderCompleted"}}
]
},
"query_temporality": "revision"
};type=application/json' \
-F 'wasm=@target/wasm32-wasip1/release/customer_profile.wasm;type=application/wasm'

Metadata Fields:

FieldRequiredDescription
state_view_nameYesUnique name for the state view
descriptionNoHuman-readable description
content_typeYesMIME type of rendered state (e.g., application/json)
content_schemaNoURI to content schema (e.g., JSON Schema URL)
event_selectorYesSelector defining which events the view processes
query_temporalityYesrevision or effective_timestamp
source_code_uriNoURI to source code repository

Response: 201 Created with redirect to the admin page for the new state view version.

use evidentsource_domain::query::{UnboundEventSelector, QueryTemporality};
let wasm_bytes = std::fs::read("customer-profile.wasm")?;
let view = app.publish_state_view_definition(
"my-events", // database
"customer-profile", // view name
Some("Aggregates customer order history"), // description
&UnboundEventSelector::Or(vec![ // event selector
UnboundEventSelector::ByType("CustomerCreated".into()),
UnboundEventSelector::ByType("OrderPlaced".into()),
UnboundEventSelector::ByType("OrderCompleted".into()),
]),
&QueryTemporality::TransactionTime, // ordering
"application/json", // content type
None, // schema URL
None, // source code URI
wasm_bytes, // WASM component
).await?;
println!("Published State View version {}", view.version());

The protobuf definition for publishing State Views will be enhanced in the service definition.

State Views subscribe to events using selectors:

// Single type
UnboundEventSelector::ByType("OrderCreated".into())
// Multiple types (OR)
UnboundEventSelector::Or(vec![
UnboundEventSelector::ByType("OrderCreated".into()),
UnboundEventSelector::ByType("OrderUpdated".into()),
])
// All events from a stream
UnboundEventSelector::ByStream("order-service".into())
// All events for a subject
UnboundEventSelector::BySubject("customer-123".into())
// Subject prefix matching
UnboundEventSelector::BySubjectPrefix("customer-".into())
// All order events from specific services
UnboundEventSelector::And(vec![
UnboundEventSelector::Or(vec![
UnboundEventSelector::ByStream("order-service".into()),
UnboundEventSelector::ByStream("fulfillment-service".into()),
]),
UnboundEventSelector::ByTypePrefix("Order".into()),
])

State Views can order events by:

Order by when events were stored (default):

QueryTemporality::TransactionTime

Use for:

  • Audit trails
  • Replication consistency
  • System-level ordering

Order by when events actually occurred:

QueryTemporality::EffectiveTime

Use for:

  • Business-time analytics
  • Historical reconstruction
  • Time-series analysis
let database = app.latest_database("my-events").await?;
let view = app.render_state_view_at_revision(
"my-events",
"customer-profile",
1, // version
database.revision(), // latest revision
&None, // no effective time cutoff
&ParameterBindings::new(), // no parameters
).await?;
let profile: CustomerProfile = serde_json::from_slice(&view.content())?;
use evidentsource_domain::query::ParameterBindings;
let mut params = ParameterBindings::new();
params.insert("customer_id".into(), "CUST-123".into());
let view = app.render_state_view_at_revision(
"my-events",
"customer-profile",
1, // State View version
100, // Database revision 100
&None, // No effective time cutoff
&params,
).await?;
use chrono::{Utc, Duration};
let cutoff = Some(Utc::now() - Duration::days(30));
let view = app.render_state_view_at_revision(
"my-events",
"monthly-summary",
1,
latest_revision,
&cutoff, // Only events before cutoff
&params,
).await?;
  1. Compile WASM component implementing Evolve interface
  2. Publish to database with metadata
  3. Receive version number
// Activate a specific version
let active = app.activate_state_view_definition_version(
"my-events",
"customer-profile",
1, // version to activate
Some(StateViewMaintenanceMode::Eager), // maintenance mode
).await?;

Maintenance modes:

  • Lazy: Compute on demand (default)
  • Eager: Pre-compute and cache
  • Scheduled: Update on schedule
// Deactivate a version
let inactive = app.deactivate_state_view_definition_version(
"my-events",
"customer-profile",
1, // version to deactivate
).await?;

State Views support multiple versions:

  • Test new versions in parallel
  • Gradual rollout
  • Rollback capability
  • A/B testing
use serde::{Deserialize, Serialize};
#[derive(Default, Serialize, Deserialize)]
struct CustomerProfile {
customer_id: String,
total_orders: u32,
total_spent: f64,
last_order_date: Option<String>,
}
wit_bindgen::generate!({
world: "state-view",
exports: {
world: CustomerProfileView,
},
});
struct CustomerProfileView;
impl Guest for CustomerProfileView {
fn evolve(state: Vec<u8>, event: Vec<u8>) -> Vec<u8> {
let mut profile: CustomerProfile = if state.is_empty() {
CustomerProfile::default()
} else {
serde_json::from_slice(&state).unwrap()
};
let event: CloudEvent = serde_json::from_slice(&event).unwrap();
match event.ty.as_str() {
"CustomerCreated" => {
profile.customer_id = event.subject.unwrap();
}
"OrderPlaced" => {
let order: OrderPlaced = serde_json::from_value(event.data).unwrap();
profile.total_orders += 1;
profile.total_spent += order.total;
profile.last_order_date = Some(event.time);
}
_ => {}
}
serde_json::to_vec(&profile).unwrap()
}
fn query(state: Vec<u8>, parameters: Vec<u8>) -> Vec<u8> {
// Apply parameter-based filtering if needed
state
}
}
target/wasm32-wasi/release/customer_profile.wasm
# Compile Rust to WASM component
cargo component build --release

State Views support HTTP caching:

GET /api/v1/db/my-events/100/state-views/customer-profile/v1?customer_id=CUST-123
If-None-Match: "revision-100-v1-hash"
304 Not Modified

State Views compute incrementally:

  1. Load previous state (if cached)
  2. Apply new events since last computation
  3. Cache result for next query

Multiple State View versions can process in parallel:

  • Different versions of same view
  • Multiple views on same events
  • Independent state computation

Common State View errors:

ErrorDescriptionResolution
StateViewNotFoundView doesn’t existVerify name and version
WasmExecutionErrorComponent failedCheck WASM logs
InvalidParametersBad parameter bindingsVerify parameter names
RevisionNotFoundRevision doesn’t existUse valid revision
use evidentsource_api::DatabaseQueryError;
match app.render_state_view_at_revision(...).await {
Ok(view) => process_view(view),
Err(DatabaseQueryError::StateViewNotFound(name, version)) => {
eprintln!("State View {}@{} not found", name, version);
}
Err(DatabaseQueryError::WasmExecutionError(err)) => {
eprintln!("WASM execution failed: {}", err);
}
Err(e) => eprintln!("Failed to render view: {}", e),
}
  1. Idempotent Evolution: Ensure evolve is deterministic
  2. Efficient State: Keep state size reasonable
  3. Version Carefully: Test new versions before activation
  4. Parameter Validation: Validate parameters in query function
  5. Error Recovery: Handle malformed events gracefully