
Eventmodeling Optimizing Stream Design
- 20 installs
- 10 repo stars
- Updated July 21, 2026
- trogonstack/agentskills
Helps with design & ui/ux tasks.
About
eventmodeling-optimizing-stream-design is a Claude Code skill in the Design & UI/UX category.
- eventmodeling-optimizing-stream-design
- Design & UI/UX
- AI-coding skill
Eventmodeling Optimizing Stream Design by the numbers
- 20 all-time installs (skills.sh)
- Ranked #1,376 of 1,880 Design & UI/UX skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/trogonstack/agentskills --skill eventmodeling-optimizing-stream-designAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 20 |
|---|---|
| repo stars | ★ 10 |
| Last updated | July 21, 2026 |
| Repository | trogonstack/agentskills ↗ |
What it does
Helps with design & ui/ux tasks.
Files
Optimizing Stream Design
Interview Phase (Optional)
When to Interview: Skip if the user has specified: expected event frequency, stream lifetime, and growth patterns. Interview when stream length concerns exist but growth estimates are unclear.
Interview Strategy: Establish growth expectations and performance requirements before recommending snapshotting. Most snapshotting proposals stem from poor stream boundary design, not genuine volume — surface the estimates first to distinguish real performance concerns from design problems.
Critical Questions
1. Growth Estimates (Impact: Determines if snapshotting is needed or if stream design should change)
- Question: "Estimate events: (A) Per entity per day, (B) Lifetime total, (C) Growth over years. Example: 5-10 events/order, 1-10 million orders/year?"
- Why it matters: Growth estimates reveal if streams will genuinely be too long or if design is wrong
- Follow-up triggers: If estimates exceed 300 events per stream lifetime → ask "Is the stream identity correct? Could this stream be split by a narrower business entity?"
2. Performance SLAs (Impact: Determines acceptable latency and snapshotting decisions)
- Question: "Performance requirements? (A) <100ms read latency, (B) <1s acceptable, (C) Eventual consistency OK?"
- Why it matters: Strict SLAs might need snapshotting; loose SLAs often don't
- Follow-up triggers: If (A) → ask "Which commands specifically need sub-100ms replay? Are those commands reading from a read model or replaying the stream directly?"
Interview Flow
Conditional Entry:
If user has provided:
- Event frequency estimate (per entity per day or per transaction)
- AND stream lifetime estimate (months or years)
- AND read latency SLA (or confirmation that eventual consistency is acceptable)
Then: Skip interview, proceed directly to stream analysis
Else: Conduct interviewPhase 1: Growth Estimation (Question 1)
- Establish per-entity event volume
- Project lifetime stream length using the estimation formula
- Determine whether redesign or snapshotting analysis is warranted
Phase 2: SLA Requirements (Question 2)
- Identify read latency requirements per command
- Determine whether read models or direct stream replay satisfies the SLA
- Establish whether snapshotting is justified by SLA alone
Capturing Interview Findings
Append findings to the project's event modeling file:
File: .trogonai/interviews/[project-name]/EVENTMODELING.md
Use Write tool to add/update this section:
## Optimizing Stream Design (eventmodeling-optimizing-stream-design)
### Growth Estimates
[From Q1: Events per entity per day, lifetime total, annual growth]
### Performance SLAs
[From Q2: Latency requirements per command or view]
### Optimization Decisions
- Streams requiring redesign: [list or "None"]
- Streams where snapshotting is justified: [list or "None"]
- Streams within acceptable bounds: [list]Update Interview Trail:
| Optimization | eventmodeling-optimizing-stream-design | Done | Stream growth estimates, SLA review, snapshotting decisions |---
Stream Design Optimization
Purpose: Optimize event stream design by validating stream boundaries, estimating growth, and making snapshotting decisions based on design quality—not just size.
Applies To: Any domain - e-commerce, banking, SaaS, marketplace, healthcare, etc.
When to Use:
- After defining event streams in domain analysis
- When concerned about stream length or performance
- Before implementing to validate stream design
- During performance planning to determine snapshotting strategy
- When redesigning streams for scalability
What It Does: 1. Analyzes event stream design for proper event organization 2. Estimates stream growth over time 3. Identifies when snapshotting is genuinely needed vs. design issue 4. Recommends optimal stream identity boundaries 5. Balances performance optimization against complexity 6. Provides snapshotting strategy without over-engineering
---
Core Principle: Design First, Snapshot Second
Golden Rule:
If you find yourself needing to snapshot because the stream is too long, first ask: "Is my stream identity wrong?" Usually, the answer is yes.
Snapshotting is a performance optimization, not a design problem. Good stream design (proper identity boundaries) often eliminates the need for snapshotting entirely.
---
Stream Design Analysis Framework
1. Estimate Stream Growth
Formula:
Estimated Stream Length (total events/instance) =
Events Per Aggregate Instance Per Year (events/instance/year)
× Lifetime of Instance (years)
× Annual Growth Factor (dimensionless year-over-year multiplier ≥ 1.0)Quick Examples:
E-commerce Order: 8 events/year × 1.5 year lifetime = 8-16 events → NOT NEEDED
Banking Account: 100-200 events/year × 10 years = 1000-2000 events → CONSIDER AT 1000+
Order Processing: 100+ events/year × 5 years = 300-500+ events → PROBABLY NEEDED
SaaS User: 12-60 events/year × 5 years = 60-300 events → RARELY NEEDED
2. Identify Stream Length Categories
| Length | Status | Action | Snapshotting |
|---|---|---|---|
| < 50 events | IDEAL | Keep as-is | NOT NEEDED |
| 50-100 events | GOOD | Monitor growth | NOT NEEDED |
| 100-300 events | ACCEPTABLE | Review boundary | CONSIDER if replayed |
| 300-1000 events | LONG | REDESIGN first | Only last resort |
| 1000+ events | CRITICAL | REDESIGN required | Won't help |
---
Quick Decision Matrix
| Stream Length | Read Pattern | Frequency | Action |
|---|---|---|---|
| < 50 | Any | Any | IDEAL - Keep as-is |
| 50-100 | Any | Any | Good - Monitor |
| 100-300 | From Model | Any | OK - No snapshot |
| 100-300 | Stream Replay | Low | OK - Monitor |
| 100-300 | Stream Replay | High | REDESIGN |
| 300-1000 | From Model | Any | OK - No snapshot |
| 300-1000 | Stream Replay | Any | REDESIGN |
| 1000+ | Any | Any | CRITICAL - REDESIGN |
---
Reference Files
Aggregate Boundary Design: See patterns.md for:
- 5 aggregate patterns (single entity, composite, collections, event logs, historical)
- Stream size decision tree
- Red flags that indicate redesign needed
- Tips for optimal stream design
Snapshotting Strategy: See snapshotting.md for:
- Criteria for when snapshotting is truly needed
- Context-based decision thresholds
- Snapshot frequency, versioning, and cleanup strategies
- Cost-benefit analysis
Domain-Specific Guidance: See domain-patterns.md for:
- E-commerce patterns (orders, carts, accounts)
- Banking patterns (accounts, transactions, loans)
- SaaS patterns (subscriptions, workspaces, data collections)
- Implementation checklist
---
Key Insights
Why Snapshotting Usually Isn't the Answer
Before implementing snapshotting, ask:
1. Can I split this aggregate into smaller ones?
→ YES: Do that instead. Simpler, better design.
2. Can I reduce event granularity?
→ YES: Batch events or create coarser state changes.
3. Am I using a read model for this aggregate?
→ NO: Create a read model (cached projection).
Stream size becomes irrelevant.
4. Have I measured actual replay latency?
→ NO: Measure first. Most systems exceed expectations.
If ANY of these is YES, do that before snapshotting.
Only after exhausting design improvements, consider snapshots.The Snapshotting Trade-off
Snapshotting Complexity ≈ 2-3x Complexity of Better Design
Before snapshot: 50 lines of code, simple, testable
With snapshots: 150+ lines, versioning, recovery logic, testing matrix
Better to redesign and keep streams < 300 events.---
Quality Checklist
- [ ] Each stream is identified by a business entity identity (e.g.,
orderId), not a category or type - [ ] No stream grows unboundedly without a design reason — event frequency and stream lifetime estimated
- [ ] Streams under 1000 events require no snapshotting justification
- [ ] If snapshotting is proposed, all simpler alternatives (split stream, shorter lifetime) have been eliminated first
- [ ] Command handler state is reconstructed from stream events — no persistent state stored outside the stream
- [ ] Each stream can be independently versioned and replayed without affecting other streams
Domain-Specific Stream Size Patterns
Contents
- E-commerce patterns
- Banking patterns
- SaaS patterns
- Implementation checklist
---
E-commerce Domain
Order Aggregate:
Events: 5-20
Lifetime: 1-3 years
Frequency: 1 event per few days
Stream Length: 8-60 events
Snapshotting: NOT NEEDED
Reason: Short entity lifetime, low frequency, few state changesShopping Cart Aggregate:
Events: 5-50+ (add/remove items many times)
Lifetime: 30 minutes to 2 years (varies widely)
Frequency: 1-10 events per hour (if active)
Stream Length: 10-500+ events (depends on user behavior)
Snapshotting: RARELY (only for frequent shoppers)
Strategy: Split abandoned vs. active carts if too longUser Account Aggregate:
Events: 2-10 per year (profile updates, settings changes)
Lifetime: 5-10+ years
Frequency: Very low (events measured in months apart)
Stream Length: 10-100 events
Snapshotting: NOT NEEDED
Reason: Infrequent events, long lifetime, many separate streams---
Banking Domain
Account Aggregate:
Events: 50-500+ per year (deposits, withdrawals, fees)
Lifetime: 10-50+ years
Frequency: 0.1-2 events per day
Stream Length: 500-25,000+ events
Snapshotting: MAYBE (at 5000+)
Strategy: Consider splitting by time period or account type
Alternative: Snapshotting might be justified for regulatory access needsTransaction Aggregate:
Events: 1-5 (Requested → Processing → Settled)
Lifetime: 1-2 months (then archived)
Frequency: Single transaction, short lifecycle
Stream Length: 2-5 events
Snapshotting: NEVER NEEDED
Reason: Tiny, immutable after completionLoan Aggregate:
Events: 100-500+ (payments, rate changes, modifications)
Lifetime: 5-30 years
Frequency: 1-5 events per month
Stream Length: 1000-10,000+ events
Snapshotting: CONSIDER AT 5000
Strategy: Split by loan product, payment period, or status
Example: ActiveLoan vs. CompletedLoan aggregates---
SaaS Domain
Subscription Aggregate:
Events: 2-20 (Created, Upgraded, Downgraded, Cancelled)
Lifetime: 1-5+ years
Frequency: 1-5 events per year
Stream Length: 5-100 events
Snapshotting: NOT NEEDED
Reason: Low frequency, well-defined lifecycleUser Workspace Aggregate:
Events: 10-100+ (members added, roles changed, settings updated)
Lifetime: 2-5+ years
Frequency: 0.5-5 events per month
Stream Length: 10-500 events
Snapshotting: NOT NEEDED
Reason: Moderate frequency, small discrete eventsData Collection Aggregate:
Events: 100-10,000+ (data points added, processed, analyzed)
Lifetime: 1-5+ years
Frequency: 1-1000+ events per day (varies wildly)
Stream Length: 100-50,000+ events
Snapshotting: PROBABLY
Strategy: Split by data type, time period, or processing stage
Question: Are all these events about the same business entity?
→ If NO, split the aggregate
→ If YES, snapshotting might be needed---
Implementation Checklist
Before implementing snapshotting, answer ALL of these:
Design Questions:
[ ] Does this aggregate have a single business identity?
[ ] Can I split this into smaller aggregates?
[ ] Are there natural lifecycle phases (archived vs. active)?
[ ] Is event granularity appropriate (not too fine)?
Performance Questions:
[ ] Have I measured replay latency?
[ ] Does latency exceed acceptable threshold?
[ ] Is the problem snaphotting will solve?
[ ] Or is it a design problem?
Cost-Benefit Questions:
[ ] How many writes per second?
[ ] How many reads per second?
[ ] What's the read latency requirement (SLA)?
[ ] Is snapshotting complexity worth the benefit?
Operational Questions:
[ ] How will I version snapshots?
[ ] How will I test snapshot recovery?
[ ] How will I monitor snapshot health?
[ ] Can I implement this given current skills?If ANY question suggests redesign is better: Redesign first, snapshot never.
If ALL questions support snapshotting: Proceed with implementation.
Aggregate Boundary Design Patterns
Contents
- Aggregate Boundary Design Patterns (5 patterns with examples)
- Stream Size Decision Tree
- Red Flags: Redesign Needed
- Tips for Optimal Stream Design
---
Aggregate Boundary Design Patterns
Pattern 1: Single Entity (Most Common)
CORRECT: One aggregate per entity
Aggregate: Order
Root Identity: orderId (e.g., 'order-123')
Entity: The specific order
Lifetime: 1-2 years
Events in stream:
1. OrderCreated (2024-01-15)
2. OrderLineAdded (2024-01-15)
3. OrderLineAdded (2024-01-15)
4. OrderConfirmed (2024-01-16)
5. PaymentProcessed (2024-01-16)
6. OrderShipped (2024-01-20)
7. OrderDelivered (2024-01-25)
Stream Length: 7 events
Snapshotting: NOT NEEDED
Identity Principle: orderId is the natural business key
Boundary: Everything about THIS specific order, nothing else
Consistency: Only one order being modified at a time---
Pattern 2: Composite Entity (Proper Composition)
CORRECT: Aggregate contains related child entities
Aggregate: Order
Root Identity: orderId (e.g., 'order-456')
Contains related children (same lifetime):
- OrderLines: 3 items
* Line 1: productId=prod-A, qty=2, price=$50
* Line 2: productId=prod-B, qty=1, price=$100
* Line 3: productId=prod-C, qty=5, price=$10
- ShippingAddress:
street: 123 Main St, City: Portland, State: OR
- PaymentInfo:
method: credit_card, amount: $400
Events in stream:
1. OrderCreated (customer-789, 3 items)
2. OrderLineAdded (item 1)
3. OrderLineAdded (item 2)
4. OrderLineAdded (item 3)
5. OrderConfirmed (payment method selected)
6. PaymentProcessed (authorization complete)
7. OrderShipped (tracking 123456)
Stream Length: 7 events
Snapshotting: NOT NEEDED (well under 1000)
Pattern: Small, bounded number of children per parent
Lifetime: Parent and all children created/destroyed together
Consistency: All modified as a unit (can't ship without payment, etc.)---
Pattern 3: Collection (ANTI-PATTERN - DO NOT USE)
WRONG: Treating a collection as aggregate
Bad Aggregate: AllOrders
Root Identity: "all-orders-collection" (artificial, meaningless)
Contains: Every order ever created
Events:
1. OrderCreated (customer-001, order-001)
2. OrderCreated (customer-002, order-002)
3. OrderCreated (customer-001, order-003)
4. OrderCreated (customer-003, order-004)
... (continues forever, unbounded)
Month 1: 50,000 events
Year 1: 600,000 events
Year 5: 3,000,000 events
Stream Length: 1,000,000+ events
Snapshotting: Doesn't help - design is fundamentally wrong
Problems with this approach:
- No single business identity (it's a collection, not an entity)
- Stream grows unbounded (can never achieve performance SLA)
- Snapshotting won't fix it (snapshot is also 1M+ events)
- Can't split or scale
- Every write goes to same stream (contention)
Solution: Use a projection/read model query instead, not an aggregate
- Query: "GetAllOrdersByCustomer(customer-id)"
- Query: "GetOrdersByStatus(status)"
- Rebuild from individual Order streams on-demand---
Pattern 4: Event Log (ANTI-PATTERN - DO NOT USE)
WRONG: Using aggregate as event log
Bad Aggregate: SystemLog
Root Identity: "system-log" (meaningless placeholder)
Contains: Every system event imaginable
Events:
1. UserLoggedIn (user-123)
2. OrderCreated (order-456)
3. PaymentProcessed (payment-789)
4. InventoryUpdated (sku-101)
5. UserLoggedOut (user-123)
6. UserLoggedIn (user-223)
... (grows indefinitely, no pattern)
Per Day: 100,000+ events
Per Year: 36,500,000+ events
Stream Length: 10,000,000+ events
Snapshotting: Impossible - design is fundamentally broken
Problems with this approach:
- No business identity (log of everything)
- Events unrelated to each other (mixing user, order, payment, inventory)
- No consistency boundary (user login != order creation)
- Can't answer "what's the state of X?" (too mixed)
- Contention: every subsystem writing to same stream
- Can't replay meaningfully (mixed concerns)
Solution: Use separate event logs or time-series database
- Keep dedicated event streams: Order, Payment, Inventory, User
- Use time-series DB for metrics/logs: Prometheus, DataDog, ELK
- Query system logs separately from domain events---
Pattern 5: Historical Aggregate (GOOD - When Needed)
CORRECT: Keep historical data for audit/compliance
Aggregate: ArchivedOrder
Root Identity: archivedOrderId (e.g., 'archived-order-001')
Purpose: Regulatory compliance (7-year retention)
Contains: Snapshot + audit trail of an order
Events:
1. OrderArchived (original order-123 on 2023-12-31)
- reason: compliance_retention
- originalData: { id, customerId, items, total, dates }
2. AuditLogAdded (accessed by accounting, 2024-01-15)
- accessor: accounting@company.com
- action: viewed for tax audit
3. AuditLogAdded (accessed by auditor, 2024-02-01)
- accessor: auditor@firm.com
- action: reviewed for compliance
... (additional audit entries over time)
Lifetime: 7 years (regulatory requirement)
Stream Length: 500-2000 events (audit entries added slowly)
Snapshotting: Not needed (historical, not active)
Key architectural principles:
- Completely separate from active Order aggregate
- Active Order is for current business operations
- Archived Order is immutable historical record
- Different access patterns, different SLAs---
Stream Size Decision Tree
Use this to decide if your streams are properly designed:
Does your stream have a natural business identity?
NO → This is not an aggregate, it's a log/report
SOLUTION: Use read model/projection, not aggregate
YES → How many events does it accumulate?
< 100 events
PERFECT: No optimization needed
100-1000 events
Is it growing because of high frequency?
NO → GOOD: Probably well-designed
YES → MONITOR: Watch for growth
Does each event represent a meaningful state change?
YES → GOOD: Healthy stream
NO → REDESIGN: Too granular events
1000-5000 events
Can you split this aggregate?
YES → REDESIGN: Do it now
Examples: User → UserProfile + UserSessions
Order → Order + OrderLineItems
NO → Is read frequency high (> 10/sec)?
YES → Consider snapshotting at 5000
NO → ACCEPTABLE: Leave as-is
Is latency critical (< 100ms)?
YES → MONITOR: Measure replay time
NO → ACCEPTABLE: No snapshotting needed
5000-10000 events
This is a design problem → REDESIGN
OR business justifies complexity → Snapshot at 5000
Questions before snapshotting:
Can I split aggregate? (usually YES)
Can I reduce event granularity? (sometimes)
Am I using a read model for this aggregate? (maybe not)
If all NO → Then snapshot is justified
> 10000 events
CRITICAL: Redesign required
This is NOT a properly designed aggregate
Snapshotting won't save you
Root cause: Aggregate boundary is wrong---
Red Flags: Redesign Needed (Not Snapshotting)
If your stream exhibits ANY of these, snapshotting won't help—you need to redesign:
Red Flag 1: Stream growing > 1000 events/day
Cause: Events are too granular
Solution: Batch events or coarsen granularity
Example: "UserClickedButton" → "UserCompletedTask" (higher level)
Red Flag 2: Thousands of events but no business meaning
Cause: Treating log as aggregate
Solution: Use read model/query instead of aggregate
Example: "SystemMetricRecorded" → Use time-series database
Red Flag 3: Stream contains unrelated entities
Cause: Aggregate boundary is wrong
Solution: Split into separate aggregates
Example: "AllOrders" → "Order" per customer
Red Flag 4: Snapshot is 80% of the stream size
Cause: Snapshot isn't helping
Solution: Re-examine aggregate boundary
Example: If snapshot is 800 events and deltas 100, redesign
Red Flag 5: Can't explain what business question the stream answers
Cause: Not a real aggregate
Solution: Convert to read model/projection
Example: "SystemEvents" → Query specific streams
Red Flag 6: Stream length doubles every 6 months
Cause: Exponential growth pattern
Solution: Likely aggregate boundary issue
Example: Split by time period: 2024-Orders vs. 2025-Orders---
Tips for Optimal Stream Design
1. Favor Redesign Over Snapshotting
Cost: Redesign effort < Snapshotting maintenance
Quality: Better design > Better optimization
Future: Smaller streams are easier to scale2. Understand Event Granularity
RIGHT: One event per meaningful state change
WRONG: Multiple events per semantic operation
Example: "UserUpdatedProfile" (1 event)
NOT: "FirstNameChanged", "LastNameChanged", ... (N events)3. Split When Possible
AllOrders (growing unbounded)
Order (per order)
OrderLine (per line item)
UserAccount (everything about user)
UserProfile (personal info)
UserPreferences (settings)
UserSessions (login history)4. Archive Old Data
Keep everything in active aggregate
Move completed/closed data to archive aggregate
Example:
- ActiveSubscription (current state)
- ArchivedSubscription (after cancelled)5. Measure Before Optimizing
Assume snapshotting is needed
Measure replay latency first
Only snapshot if measurement justifies itSnapshotting Strategy
Contents
- Criteria for Snapshotting
- Context-Based Decision Thresholds
- Snapshotting Downsides
- Snapshot Frequency & Versioning
- Snapshot Cleanup Strategies
---
When Snapshotting is Actually Needed
Conservative First Approach: Keep Streams Short
Default Principle: Prefer shorter streams over snapshotting
- Shorter streams = simpler code, easier debugging, fewer bugs
- Snapshotting = added complexity that's hard to get right
- Most systems don't actually need snapshotting if designed well
Criteria: Snapshotting Only When ALL Are True
1. Stream length > 100 events (conservative default) AND
2. You've measured latency and it exceeds SLA AND
3. The latency problem IS stream replay (not read model) AND
4. Aggregate boundary is already optimal (can't split further) AND
5. You have operational capability to maintain snapshotsIf ANY criteria fail: Don't snapshot. Redesign instead.
Context-Based Thresholds (Ask These Questions)
Instead of assuming a fixed number, ask about your actual business context:
Question 1: How is this aggregate read?
"From stream replay" (loaded every time)
→ Conservative threshold: 50-100 events
→ Reason: Replay latency compounds
"From read model/cache" (projection loaded once)
→ Conservative threshold: Not relevant (stream size doesn't matter!)
→ Reason: You're not replaying on each readQuestion 2: What's your read latency requirement?
"Immediate/real-time" (< 50ms, user-facing)
→ Conservative threshold: 50 events max
→ Reason: Strict SLA, no room for slowness
"Normal web response" (100-500ms, typical page load)
→ Conservative threshold: 100-300 events
→ Reason: Some latency acceptable if not critical path
"Background/batch operations" (seconds to minutes)
→ Conservative threshold: Not a concern
→ Reason: Speed doesn't matter for batch workQuestion 3: How often is this aggregate read?
"Very frequent" (> 100 reads/second)
→ Use threshold ÷ 5 (contention matters more)
→ Reason: Concurrent replays degrade badly
"Normal frequency" (1-10 reads/second)
→ Use stated threshold
→ Reason: Single-digit concurrency manageable
"Rare" (< 1 read per minute)
→ Use threshold × 2-3 (who cares about latency?)
→ Reason: Speed doesn't matter if rarely accessedIf User Doesn't Know (Most Common Case)
Guidance: Be conservative AND use conversation context
Default position:
→ Start with 100 events as safe threshold
→ This prevents 90% of problems
→ Better to redesign early than add snapshotting later
If user says "I don't know my requirements":
→ Use context from earlier conversation
→ Review: What did domain analysis say?
→ Check: What's the business criticality?
→ Ask: Is this user-facing or backend?
Example Decision Logic:
Domain: E-commerce (user-facing) + No explicit SLA
→ Use 100-event threshold (conservative for user-facing)
Domain: Bank transfers (critical) + No explicit SLA
→ Use 50-event threshold (very conservative, safety margin)
Domain: Analytics (batch) + No explicit SLA
→ Use 1000+ threshold (performance doesn't matter)---
Snapshotting Downsides to Consider
Complexity Cost:
- Extra code path (snapshot loading logic)
- Testing complexity (snapshot + delta replay)
- Snapshot versioning challenges
- Potential for bugs in snapshot recovery
Operational Cost:
- Storage overhead (original events + snapshots)
- Cleanup and archival strategies
- Debugging difficulty (was it the snapshot?)
- Migration burden if snapshot format changes
Performance Cost:
- Snapshot creation cost
- Storage I/O for snapshots
- Memory usage during snapshot loading
- Synchronization between events and snapshots
Rule of Thumb:
Complexity of snapshotting ≈ 2-3x complexity of solving with better design---
Snapshotting Strategy (When Actually Needed)
If You Decide Snapshotting is Necessary:
Snapshot Frequency Decision:
Rule: Snapshot every N events where N = √(Total Estimated Events)
Example:
If stream will eventually reach 10,000 events:
N = √10,000 = 100
Snapshot every 100 events
Result: 100 snapshots + max 100 events to replay = manageable
If stream reaches 100,000 events (red flag):
N = √100,000 = 316
Snapshot every 316 events
Result: 316 snapshots = storage issue, redesign neededBetter Rule: Snapshot when read latency exceeds acceptable threshold
Measure:
1. Measure event replay time for current stream length
2. If > acceptable latency (e.g., 50ms), snapshot
3. Snapshot frequency = whatever makes latency acceptable
4. Re-measure after snapshot implementation
Example:
- Stream: 2000 events
- Replay time: 120ms (acceptable if reads are occasional)
- Snapshot needed? NO
- Decision: Monitor, implement snapshotting only if latency > 200msSnapshot Versioning:
DON'T: Version snapshots, migrate old formats
DO: Version aggregates instead
Pattern:
Aggregate Version 1: Stream 1
Aggregate Version 2: New Stream with different structure
Reason: Snapshots are just optimization, not part of model
If snapshot format needs to change, it means your aggregate changed
→ Create new aggregate version with new stream insteadSnapshot Cleanup:
Good Strategy: Snapshot + Event Log
- Keep original events (immutable, source of truth)
- Keep snapshots (performance optimization)
- No special cleanup needed (both are authoritative together)
Bad Strategy: Snapshot + Purge Old Events
- Destroys event history
- Makes auditing impossible
- Complicates recovery
- Only do if regulatory rules require it
Alternative: Archive Old Events
- Keep all events for audit trail
- Archive to slower storage if needed
- Snapshot in hot storage for performance
- Best of both worlds