State Views Overview
State Views API Overview
Section titled “State Views API 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.
Concepts
Section titled “Concepts”The Evolve Pattern
Section titled “The Evolve Pattern”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
WebAssembly Components
Section titled “WebAssembly Components”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
Publishing State Views
Section titled “Publishing State Views”HTTP API
Section titled “HTTP API”State Views are published via multipart/form-data POST request with two parts:
metadata: JSON containing the state view configurationwasm: The compiled WebAssembly component binary
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:
| Field | Required | Description |
|---|---|---|
state_view_name | Yes | Unique name for the state view |
description | No | Human-readable description |
content_type | Yes | MIME type of rendered state (e.g., application/json) |
content_schema | No | URI to content schema (e.g., JSON Schema URL) |
event_selector | Yes | Selector defining which events the view processes |
query_temporality | Yes | revision or effective_timestamp |
source_code_uri | No | URI to source code repository |
Response: 201 Created with redirect to the admin page for the new state view version.
Rust API
Section titled “Rust API”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());gRPC API
Section titled “gRPC API”The protobuf definition for publishing State Views will be enhanced in the service definition.
Event Selectors
Section titled “Event Selectors”State Views subscribe to events using selectors:
By Event Type
Section titled “By Event Type”// Single typeUnboundEventSelector::ByType("OrderCreated".into())
// Multiple types (OR)UnboundEventSelector::Or(vec![ UnboundEventSelector::ByType("OrderCreated".into()), UnboundEventSelector::ByType("OrderUpdated".into()),])By Stream
Section titled “By Stream”// All events from a streamUnboundEventSelector::ByStream("order-service".into())By Subject
Section titled “By Subject”// All events for a subjectUnboundEventSelector::BySubject("customer-123".into())
// Subject prefix matchingUnboundEventSelector::BySubjectPrefix("customer-".into())Complex Selectors
Section titled “Complex Selectors”// All order events from specific servicesUnboundEventSelector::And(vec![ UnboundEventSelector::Or(vec![ UnboundEventSelector::ByStream("order-service".into()), UnboundEventSelector::ByStream("fulfillment-service".into()), ]), UnboundEventSelector::ByTypePrefix("Order".into()),])Query Temporality
Section titled “Query Temporality”State Views can order events by:
Transaction Time
Section titled “Transaction Time”Order by when events were stored (default):
QueryTemporality::TransactionTimeUse for:
- Audit trails
- Replication consistency
- System-level ordering
Effective Time
Section titled “Effective Time”Order by when events actually occurred:
QueryTemporality::EffectiveTimeUse for:
- Business-time analytics
- Historical reconstruction
- Time-series analysis
Rendering State Views
Section titled “Rendering State Views”At Latest Revision
Section titled “At Latest Revision”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())?;At Specific Revision
Section titled “At Specific Revision”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 ¶ms,).await?;With Effective Time Cutoff
Section titled “With Effective Time Cutoff”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 ¶ms,).await?;State View Lifecycle
Section titled “State View Lifecycle”Publishing
Section titled “Publishing”- Compile WASM component implementing Evolve interface
- Publish to database with metadata
- Receive version number
Activation
Section titled “Activation”// Activate a specific versionlet 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 cacheScheduled: Update on schedule
Deactivation
Section titled “Deactivation”// Deactivate a versionlet inactive = app.deactivate_state_view_definition_version( "my-events", "customer-profile", 1, // version to deactivate).await?;Versioning
Section titled “Versioning”State Views support multiple versions:
- Test new versions in parallel
- Gradual rollout
- Rollback capability
- A/B testing
Implementation Example
Section titled “Implementation Example”WASM Component (Rust)
Section titled “WASM Component (Rust)”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 }}Compilation
Section titled “Compilation”# Compile Rust to WASM componentcargo component build --release
Caching and Performance
Section titled “Caching and Performance”ETag Support
Section titled “ETag Support”State Views support HTTP caching:
GET /api/v1/db/my-events/100/state-views/customer-profile/v1?customer_id=CUST-123If-None-Match: "revision-100-v1-hash"
304 Not ModifiedIncremental Computation
Section titled “Incremental Computation”State Views compute incrementally:
- Load previous state (if cached)
- Apply new events since last computation
- Cache result for next query
Parallel Processing
Section titled “Parallel Processing”Multiple State View versions can process in parallel:
- Different versions of same view
- Multiple views on same events
- Independent state computation
Error Handling
Section titled “Error Handling”Common State View errors:
| Error | Description | Resolution |
|---|---|---|
StateViewNotFound | View doesn’t exist | Verify name and version |
WasmExecutionError | Component failed | Check WASM logs |
InvalidParameters | Bad parameter bindings | Verify parameter names |
RevisionNotFound | Revision doesn’t exist | Use 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),}Best Practices
Section titled “Best Practices”- Idempotent Evolution: Ensure
evolveis deterministic - Efficient State: Keep state size reasonable
- Version Carefully: Test new versions before activation
- Parameter Validation: Validate parameters in
queryfunction - Error Recovery: Handle malformed events gracefully
Next Steps
Section titled “Next Steps”- Learn Defining State Views
- Explore Querying State Views
- Understand Maintenance Modes
- Implement State Changes for commands