| .. | ||
| ephemeral-streams.md | ||
| events-and-workflows.md | ||
| getting-started.md | ||
| persistent-streams.md | ||
| README.md | ||
| subscriptions.md | ||
Event Streaming Fundamentals
Core concepts and patterns for event streaming.
Overview
This section covers the fundamental concepts needed to use event streaming in Svrnty.CQRS. You'll learn about stream types, event design, subscriptions, and workflows.
Topics
Getting Started
Create your first event stream and publish/consume events:
- Installation and configuration
- Publishing events to persistent streams
- Reading events from streams
- Using ephemeral streams for queuing
Persistent Streams
Event sourcing with append-only logs:
- Append-only event logs
- Offset-based reading
- Event replay capabilities
- Audit log patterns
- Event versioning
Ephemeral Streams
Message queue semantics:
- Dequeue with visibility timeout
- At-least-once delivery
- Acknowledge/nack messages
- Dead letter queue handling
- Background job processing
Events and Workflows
Designing events and workflows:
- Event naming conventions
- Event payload design
- Domain events vs integration events
- Workflow pattern
- Event metadata and correlation
Subscriptions
Subscription modes and patterns:
- Broadcast subscriptions (all events)
- Queue subscriptions (load balanced)
- Persistent vs ephemeral subscriptions
- Subscription lifecycle
- Error handling in subscriptions
Quick Reference
Stream Types
| Type | Storage | Reading | Use Case |
|---|---|---|---|
| Persistent | Append-only log | Offset-based | Event sourcing, audit logs |
| Ephemeral | Queue | Dequeue with timeout | Background jobs, notifications |
Delivery Modes
| Mode | Semantics | Consumer Behavior |
|---|---|---|
| Broadcast | At-least-once | All consumers receive all events |
| Queue | Exactly-once per group | One consumer per event (load balanced) |
Common Operations
Persistent Stream:
// Append events
await store.AppendAsync("orders", new[] { orderPlaced });
// Read from offset
await foreach (var evt in store.ReadStreamAsync("orders", fromOffset: 0))
{
ProcessEvent(evt);
}
Ephemeral Stream:
// Enqueue message
await store.EnqueueAsync("email-queue", sendEmailCommand);
// Dequeue with timeout
var msg = await store.DequeueAsync("email-queue", TimeSpan.FromMinutes(5));
await ProcessMessageAsync(msg);
await store.AcknowledgeAsync("email-queue", msg.MessageId);
Key Concepts
Events are Immutable
Once appended, events cannot be modified:
// ✅ Good - Append new compensating event
await store.AppendAsync("orders", new[] {
new OrderCancelledEvent { OrderId = 123, Reason = "Customer request" }
});
// ❌ Bad - Never modify existing events
// Events are immutable!
Events Record Facts
Events describe things that have happened (past tense):
// ✅ Good - Past tense, describes what happened
public record OrderPlacedEvent
{
public int OrderId { get; init; }
public DateTimeOffset PlacedAt { get; init; }
}
// ❌ Bad - Present tense or imperative
public record PlaceOrderEvent { }
public record OrderPlaceCommand { } // This is a command, not an event
Correlation IDs
Track related events across boundaries:
await store.AppendAsync("orders", new[]
{
new OrderPlacedEvent
{
OrderId = 123,
CorrelationId = "abc-123", // Links to original request
CausationId = commandId // Links to command that caused this
}
});
Event Design Principles
- Single Responsibility: One event type per business fact
- Self-Contained: Include all data needed to process
- Versioned: Plan for schema evolution
- Idempotent: Handlers should handle duplicates gracefully
- Descriptive: Event names clearly describe what happened
Next Steps
- Start with Getting Started for hands-on introduction
- Learn Persistent Streams for event sourcing
- Understand Ephemeral Streams for message queues
- Design events using Events and Workflows
- Configure Subscriptions for consuming events