Walkthroughs topic
DEMO_GUIDE.md
(Cell + Flow) Demo Guide
From Simple Operators to Real-World Applications
A comprehensive guide to understanding the Cell Framework through practical examples, organized from fundamental concepts to complex real-world applications.
Table of Contents
- Introduction
- Core Concepts
- Demo Learning Path
- Level 1: Foundation Operators
- Level 2: Flow Control
- Level 3: Async & Composition
- Level 4: Complex Orchestration
- Level 5: Real-World Applications
- Operator Reference
- Combining Patterns
- Common Pitfalls
- Next Steps
Introduction
The Cell Framework provides a reactive programming model where Cells hold state, Pulses carry signals, and Flow operators transform data streams. This guide walks you through the demo examples from simple operators to complex real-world applications.
What You'll Learn
- How to use each operator correctly
- When to choose one operator over another
- How to combine operators into pipelines
- How to build production-ready applications
- Common patterns and anti-patterns
The Three-Layer Architecture
Every demo follows the same architectural pattern:
| Layer | Responsibility | Examples |
|---|---|---|
| Cell | Holds state, carries data | Cell.ingress, Cell.state, Cell.observe |
| Flow | Transforms, filters, controls timing | Flow.map, Flow.filter, Flow.debounce |
| Observer | Side effects, UI updates, logging | Cell.observe |
The Golden Rule: Flow transforms data. Cell holds state. Observer handles side effects. Never mix these responsibilities.
Core Concepts
What is a Cell?
A Cell is a reactive node that:
- Holds state (
Cell.state) - Accepts external input (
Cell.ingress) - Broadcasts changes to observers
- Validates incoming data (
TestCell)
// A simple state cell
final counter = Cell.state<int>(
initial: 0,
evolve: (host, pulse) => Pulse(host.value + 1),
);
// Read the value
print(counter.cell.value); // 0
// Update the value
await counter.updateAsync(5);
What is a Pulse?
A Pulse is an immutable message that carries:
- Payload: The actual data
- Type: Semantic category for routing
- Priority: Execution urgency (0-100)
- Context: Provenance metadata
- Trace: Transformation history
final pulse = Pulse.governed<String>(
payload: 'Hello, World!',
type: 'message',
priority: 60,
context: PulseContext.userAction(
actor: 'user_123',
reason: 'Sending message',
),
);
What is Flow?
Flow provides operators that transform pulses:
- Map: Transform each value
- Filter: Remove unwanted values
- Debounce: Wait for silence
- Throttle: Limit frequency
- AsyncMap: Handle async operations
final result = searchInput.cell
.debounce(duration: Duration(milliseconds: 300))
.asyncMap((query) => api.search(query))
.filter((results) => results.isNotEmpty);
Demo Learning Path
Level 1: Foundation Operators (Start Here)
├── Cell.state - State Management
├── Cell.ingress - Input Gateway
├── Cell.observe - Side Effects
├── Flow.map - Transformation
└── Flow.filter - Validation
Level 2: Flow Control
├── Flow.debounce - Search-as-you-type
├── Flow.throttle - Rate Limiting
├── Flow.distinct - Noise Reduction
└── Flow.tap - Logging & Analytics
Level 3: Async & Composition
├── Flow.fromFuture - Legacy API Bridge
├── Flow.fromStream - Real-time Data
├── Flow.asyncMap - Parallel Processing
├── Flow.switchMap - Dynamic Sources
└── Flow.mergeWith - Stream Merging
Level 4: Complex Orchestration
├── Flow.buffer - Batch Processing
├── Flow.retry - Fault Tolerance
├── Flow.timeout - Deadlines
├── Flow.combineLatest - Real-time Sync
└── Flow.exhaustMap - Submit Protection
Level 5: Real-World Applications
├── Pharmacy Dispense - Transactions
├── ICU Alarm - Medical Monitoring
├── Forensic Pipeline - Audit & Compliance
└── Data Quality - Monitoring & Validation
Level 1: Foundation Operators
1. Cell.state — State Management
File: Built into the Cell Framework core
What it does: Creates a persistent state atom that can be read and updated.
When to use: Any time you need to store and manage mutable state.
final counter = Cell.state<int>(
initial: 0,
evolve: (host, pulse) => Pulse(host.value + 1),
);
// Read
print(counter.cell.value); // 0
// Update (synchronous)
counter.update(5);
// Update (asynchronous)
await counter.updateAsync(10);
// Observe changes
Cell.observe(
source: counter.cell,
effect: (pulse) => print('Counter: ${pulse.payload}'),
);
Key Insight: The evolve function defines how the state changes. It receives the current value and the incoming pulse, and returns the new value.
2. Cell.ingress — Input Gateway
File: Built into the Cell Framework core
What it does: Creates an entry point for external events to enter the reactive graph.
When to use: UI events, network callbacks, hardware interrupts.
final searchInput = Cell.ingress<String>();
// Emit from UI
searchInput.emit('dart');
// Or with async safety
await searchInput.emitAsync('flutter');
// Observe the input
Cell.observe(
source: searchInput.cell,
effect: (pulse) => print('Search: ${pulse.payload}'),
);
Key Insight: Ingress cells are stateless — they only relay events. For persistent state, use Cell.state.
3. Cell.observe — Side Effects
File: Built into the Cell Framework core
What it does: Creates a terminal observer that executes side effects when a pulse arrives.
When to use: UI updates, logging, database writes, network requests.
final observer = Cell.observe(
source: counter.cell,
effect: (pulse) {
// This runs on every state change
print('Counter changed to: ${pulse.payload}');
updateUI(pulse.payload);
},
);
// Stop observing
observer.stop();
// Start again
observer.start();
Key Insight: Observers are terminal — they don't propagate pulses. They're for side effects only.
4. Flow.map — Transformation
File: filter_data_quality_demo.dart
What it does: Transforms each value through a synchronous function.
When to use: Data formatting, type conversion, calculations.
final input = Cell.ingress<int>();
final doubled = Flow.map<int, int>(
input.cell,
project: (n) => n * 2,
);
Cell.observe(
source: doubled.cell,
effect: (pulse) => print('Doubled: ${pulse.payload}'),
);
input.emit(5); // Prints: Doubled: 10
Key Insight: Map is pure — it doesn't modify the original data, it creates new data.
5. Flow.filter — Validation
File: filter_data_quality_demo.dart
What it does: Removes values that don't satisfy a predicate.
When to use: Validation, data cleaning, quality control.
final input = Cell.ingress<int>();
final positiveOnly = Flow.filter<int>(
input.cell,
test: (n) => n > 0,
);
Cell.observe(
source: positiveOnly.cell,
effect: (pulse) => print('Positive: ${pulse.payload}'),
);
input.emit(5); // Prints: Positive: 5
input.emit(-3); // Nothing (filtered out)
input.emit(0); // Nothing (filtered out)
Key Insight: Filter returns null to drop values. The observer never sees dropped values.
Level 2: Flow Control
6. Flow.debounce — Search-as-you-type
File: stability_search_demo.dart
What it does: Waits for a period of silence before emitting the latest value.
When to use: Search boxes, auto-save, form validation.
final searchInput = Cell.ingress<String>();
final debounced = Flow.debounce<String>(
searchInput.cell,
duration: Duration(milliseconds: 300),
);
final searchResults = Flow.asyncMapLatest<String, List<String>>(
debounced.cell,
mapper: (query) async => await api.search(query),
);
// User types: 'd', 'da', 'dar', 'dart'
// Only 'dart' triggers the API call
Why this matters: Without debounce, every keystroke triggers an API call. With debounce, only the final query is processed.
// ❌ Bad: 4 API calls
searchInput.emit('d');
searchInput.emit('da');
searchInput.emit('dar');
searchInput.emit('dart');
// ✅ Good: 1 API call (after 300ms silence)
searchInput.emit('d');
searchInput.emit('da');
searchInput.emit('dar');
searchInput.emit('dart');
// Wait 300ms...
// Only 'dart' is sent to the API
7. Flow.throttle — Rate Limiting
File: throttle_rate_limiting_demo.dart
What it does: Limits the frequency of emissions to a maximum rate.
When to use: Scroll events, API rate limiting, click prevention.
final clicks = Cell.ingress<void>();
final throttled = Flow.throttle<void>(
clicks.cell,
duration: Duration(milliseconds: 300),
leading: true, // Emit first click immediately
trailing: false, // Don't emit last click
);
// User clicks 5 times in 100ms
// Only the first and last clicks are emitted
Throttle Modes:
| leading | trailing | Behavior | Use Case |
|---|---|---|---|
| true | false | First only, then silent | Click prevention |
| false | true | Last only, after window | Final state only |
| true | true | First + last | Complete handling |
| false | false | Nothing | Testing |
// Debounce vs Throttle
// Debounce: waits for silence → only final value
// Throttle: limits rate → first value, then periodic
// Search: use debounce (only final query matters)
// Scroll: use throttle (need regular updates)
8. Flow.distinct — Noise Reduction
File: distinct_noise_reduction_demo.dart
What it does: Removes consecutive duplicate values.
When to use: Sensor noise reduction, stock tickers, user activity.
final sensor = Cell.ingress<double>();
final cleanReadings = Flow.distinct<double>(
sensor.cell,
equals: (prev, next) => (next - prev).abs() < 0.2, // Custom tolerance
);
// Raw: [10.0, 10.05, 10.1, 10.15, 10.2, 50.0, 50.05, 50.1]
// Output: [10.0, 10.2, 50.0, 50.1]
// (Noise within 0.2 is filtered out)
Distinct vs Other Filters:
| Operator | Removes | Memory | Use Case |
|---|---|---|---|
| Distinct | Consecutive duplicates | O(1) | Sensor noise |
| DistinctAll | Any duplicate | O(n) | Unique IDs |
| Filter | By predicate | O(1) | Validation |
| Debounce | By time | O(1) | Search |
9. Flow.tap — Logging & Analytics
File: tap_logging_analytics_demo.dart
What it does: Injects side effects without modifying the data flow.
When to use: Logging, analytics, performance monitoring, debugging.
final actions = Cell.ingress<String>();
// Tap for logging
final logged = Flow.tap<String>(
actions.cell,
onValue: (value) => print('[LOG] $value'),
);
// Tap for analytics
final tracked = Flow.tap<String>(
logged.cell,
onValue: (value) => analytics.track(value),
);
// Tap for performance
final timed = Flow.tap<String>(
tracked.cell,
onValue: (value) => metrics.record(value),
);
// The original data flows through unchanged
// All three side effects run, but the data is identical
Tap Types:
| Tap Type | Purpose | Example |
|---|---|---|
| tap | Simple side effect | Logging |
| tapWithIndex | Position tracking | Session tracking |
| tapState | State maintenance | Running totals |
| tapAll | Full pulse access | Debugging |
Level 3: Async & Composition
10. Flow.fromFuture — Legacy API Bridge
File: from_future_forensic_pipeline_demo.dart
What it does: Bridges a single Future into the reactive graph.
When to use: Loading configuration, user profile, initial data.
final loadButton = Cell.ingress<void>();
final profile = Flow.fromFuture<UserProfile>(
loadButton.cell,
future: api.fetchUserProfile('user_123'),
);
Cell.observe(
source: profile.cell,
effect: (pulse) => print('Profile: ${pulse.payload.name}'),
);
loadButton.emit(null); // Triggers the Future
fromFuture vs deferFuture:
| Feature | fromFuture | deferFuture |
|---|---|---|
| Trigger | First pulse only | Every pulse |
| Reusability | One-time | Reusable |
| Use Case | Initialization | Per-request |
11. Flow.fromStream — Real-time Data
File: from_stream_realtime_data_demo.dart
What it does: Bridges a Dart Stream into the reactive graph.
When to use: WebSockets, SSE, sensors, file watchers.
final connect = Cell.ingress<void>();
final wsData = Flow.fromStream<Map<String, dynamic>>(
connect.cell,
stream: webSocket.stream,
);
final processed = Flow.map<Map<String, dynamic>, String>(
wsData.cell,
project: (data) => data['message'] as String,
);
Cell.observe(
source: processed.cell,
effect: (pulse) => print('Message: ${pulse.payload}'),
);
connect.emit(null); // Starts listening
Supported Stream Sources:
- WebSocket
- Server-Sent Events (SSE)
- Hardware sensors
- File watchers
- Database change streams
- Network sockets
12. Flow.asyncMap — Parallel Processing
File: async_map_parallel_search_demo.dart
What it does: Maps each value through an async function with configurable concurrency.
When to use: API calls, database queries, file I/O.
Three Strategies:
// 1. Sequential (asyncMap) - order matters
final sequential = Flow.asyncMap<int, User>(
ids.cell,
mapper: (id) async => await api.getUser(id),
);
// Output: [User1, User2, User3] (in order, one at a time)
// 2. Parallel (asyncMapConcurrent) - throughput matters
final parallel = Flow.asyncMapConcurrent<int, User>(
ids.cell,
mapper: (id) async => await api.getUser(id),
);
// Output: [User3, User2, User1] (completion order, all start at once)
// 3. Latest (asyncMapLatest) - only latest matters
final latest = Flow.asyncMapLatest<int, User>(
ids.cell,
mapper: (id) async => await api.getUser(id),
);
// Output: [User3] only (previous requests cancelled)
Choosing the Right Strategy:
| Strategy | Concurrency | Cancels | Order | Use Case |
|---|---|---|---|---|
| asyncMap | 1 (queue) | No | Input | Ordered tasks |
| asyncMapConcurrent | Unlimited | No | Completion | Parallel fetches |
| asyncMapLatest | 1 (latest) | Yes (stale) | Latest | Search-as-you-type |
13. Flow.switchMap — Dynamic Sources
File: switch_map_dynamic_dependency_demo.dart
What it does: Switches to a new data source on each pulse, cancelling the previous one.
When to use: Feature flags, user selection, A/B testing, multi-tenant.
final selector = Cell.ingress<String>();
final data = Flow.switchMap<String, String>(
selector.cell,
project: (source) {
switch (source) {
case 'api-v1': return apiV1.stream();
case 'api-v2': return apiV2.stream();
default: return fallback.stream();
}
},
);
// Select v1 → starts streaming from v1
selector.emit('api-v1');
// Select v2 → v1 is automatically cancelled, v2 starts
selector.emit('api-v2');
Use Cases:
| Use Case | Pattern |
|---|---|
| Feature flags | Switch between implementations |
| User context | Switch between user profiles |
| A/B testing | Switch between experiment variants |
| Multi-tenant | Switch between tenant data sources |
| Theme switching | Switch between theme providers |
14. Flow.mergeWith — Stream Merging
File: merge_stream_aggregation_demo.dart
What it does: Merges multiple streams into one, interleaving events.
When to use: Combining events from multiple sources.
Two Approaches:
// 1. Flow.mergeWith (built-in)
final merged = Flow.mergeWith<String>(
sourceA.cell,
others: [sourceB.cell, sourceC.cell],
);
// 2. Fan-in Pattern (more control)
final bus = Cell.ingress<String>();
_fanIn<String>([sourceA.cell, sourceB.cell, sourceC.cell], bus);
Fan-in vs mergeWith:
| Feature | mergeWith | Fan-in |
|---|---|---|
| Event delivery | May drop | Reliable |
| Source tracking | Loses | Preserves |
| Flexibility | Fixed | Customizable |
Level 4: Complex Orchestration
15. Flow.buffer — Batch Processing
File: buffer_batch_processing_demo.dart
What it does: Collects events into batches and emits them as lists.
When to use: Log batching, database bulk operations, analytics aggregation.
final events = Cell.ingress<LogEntry>();
// 1. Buffer by count
final countBatch = Flow.bufferCount<LogEntry>(
events.cell,
size: 100, // Emit when 100 logs collected
);
// 2. Buffer by time
final timeBatch = Flow.bufferTime<LogEntry>(
events.cell,
duration: Duration(seconds: 5), // Emit every 5 seconds
);
// 3. Buffer by count OR time
final comboBatch = Flow.bufferWithTimeAndCount<LogEntry>(
events.cell,
duration: Duration(seconds: 5),
count: 100, // Whichever comes first
);
// 4. Buffer when condition met
final predBatch = Flow.bufferWithPredicate<LogEntry>(
events.cell,
test: (log) => log.level == 'ERROR', // Flush on error
);
// 5. Buffer on external trigger
final trigger = Cell.ingress<void>();
final whenBatch = Flow.bufferWhen<LogEntry>(
events.cell,
closer: trigger.cell, // Flush when trigger emits
);
Buffer Strategy Comparison:
| Strategy | Trigger | Memory | Latency | Use Case |
|---|---|---|---|---|
| BufferCount | Size | Fixed | Low | Fixed-size batches |
| BufferTime | Time | Variable | High | Time-based batches |
| BufferWithTimeAndCount | Size OR time | Fixed | Low | Either condition |
| BufferWithPredicate | Condition | Variable | Low | Conditional batching |
| BufferWhen | External | Variable | Manual | Manual batching |
16. Flow.retry — Fault Tolerance
File: retry_timeout_flaky_network_demo.dart
What it does: Retries failed operations with bounded attempts.
When to use: Network calls, database operations, external APIs.
final request = Cell.ingress<String>();
final result = Flow.retry<String, UserProfile>(
request.cell,
task: (id) async => await api.getUser(id),
count: 3, // Retry up to 3 times
onError: (error, stack) => print('Error: $error'),
);
// If the first attempt fails, it retries automatically
// If all 3 attempts fail, an error pulse is emitted
Retry Patterns:
// 1. Simple retry
Retry(task, count: 3)
// 2. Retry with fallback
AsyncMapWithFallback(mapper, fallback: defaultValue)
// 3. Retry with timeout
Timeout(duration) + Retry(task, count: 3)
// 4. Exponential backoff (custom)
Retry(task, count: 3) + custom delay logic
17. Flow.timeout — Deadlines
File: retry_timeout_flaky_network_demo.dart
What it does: Enforces a deadline on async operations.
When to use: Time-sensitive operations, user experience, SLA enforcement.
final request = Cell.ingress<String>();
// Operation with timeout
final slowOp = AsyncMap<String, String>(
(id) async {
await Future.delayed(Duration(seconds: 5));
return 'Result';
},
).toHandle(source: request.cell);
// Enforce 2 second deadline
final timedOut = Timeout<String>(
Duration(seconds: 2),
onError: (error, stack) => print('Operation timed out!'),
emitErrorPulse: true,
).toHandle(source: slowOp.cell);
// Users see "Operation timed out" after 2 seconds
Timeout Strategies:
| Strategy | Behavior | Use Case |
|---|---|---|
| Timeout | Throw on timeout | Critical operations |
| TimeoutWithFallback | Use fallback on timeout | Graceful degradation |
| Timeout with Retry | Retry then timeout | Network calls |
| Deadline with Loading | Show progress then timeout | User feedback |
18. Flow.combineLatest — Real-time Sync
File: combine_latest_sync_demo.dart
What it does: Combines the latest values from multiple sources, emitting on any change.
When to use: Dashboards, form validation, portfolio aggregation.
final users = Cell.ingress<int>();
final requests = Cell.ingress<double>();
final errors = Cell.ingress<double>();
final dashboard = Flow.combineLatestWith<int, Map<String, dynamic>>(
users.cell,
others: [requests.cell, errors.cell],
combine: (userCount, latest) => {
'users': userCount,
'requests': latest[0] as double,
'errors': latest[1] as double,
'timestamp': DateTime.now(),
},
);
// All sources must have at least one value before first emission
// After that, any source change triggers a new emission
combineLatest vs Other Operators:
| Operator | Emits When | Use Case |
|---|---|---|
| combineLatestWith | ANY source changes | Real-time dashboards |
| withLatestFrom | Source changes only | Commands with state |
| zipWith | ALL sources have next value | Paired data |
| mergeWith | ANY source emits | Event interleaving |
19. Flow.exhaustMap — Submit Protection
File: exhaust_map_submit_checkout_demo.dart
What it does: Ignores new triggers while an operation is in flight.
When to use: Submit buttons, checkout, refresh, file uploads.
final submit = Cell.ingress<Order>();
final orderResult = Flow.exhaustMap<Order, String>(
submit.cell,
project: (order) async {
await processOrder(order);
return 'Order ${order.id} confirmed!';
},
);
// User clicks submit 5 times rapidly
// Only the first click is processed
// Clicks 2-5 are ignored while busy
exhaustMap vs Other Operators:
| Operator | Behavior | Use Case |
|---|---|---|
| exhaustMap | First wins, ignore rest | Submit, checkout |
| debounce | Last wins after silence | Search |
| throttle | First wins, then rate-limit | Scroll |
| switchMap | Latest wins, cancel previous | Search-as-you-type |
| concatMap | Queue all, process sequentially | Ordered processing |
Level 5: Real-World Applications
20. Pharmacy Dispense — Transactions
File: domain-cells-txApply(enhanced)-Demo.dart
What it demonstrates: How Cell, Flow, and Transactions combine for a real pharmacy system.
Key Components:
- Ingress Layer - TestCell validation on NDC codes
- Flow Layer - MapValue + Filter + Take(1) gate
- Transaction Layer - Repeatable-read isolation on stock
- Compensation - Restore pack on hardware failure
Architecture:
Technician Scan
│
▼
[Cell] gun.ingress + TestCell(ndcLike)
│
▼
[Flow] MapValue(trim/upper) + Filter(NDC) + Take(1)
│
▼
[Cell] rx.cell
│ Cell.observe
▼
fullDispense()
│
├─ [Tx] confirmPatient → patient cell
├─ [Tx] dispenseMemory → stock + label (repeatable-read)
├─ [Hardware] openDrawer → drawer cell
├─ [Hardware] printLabel → I/O
└─ [Tx] restorePack → compensation on failure
Key Lessons:
- Flow stops pulses. Transaction moves state.
- One Receptor per gun. Reset Take; do not stack
toHandle. - Repeatable-read on shelf Cells, not on gun.
- Hardware after commit → new Tx to compensate.
- TestCell on every ingress that a human or device can emit.
- Do not put
tx.begininMapValue + Filter.
21. ICU Alarm — Medical Monitoring
File: ICU-alarm-pipeline(enhanced)-Demo.dart
What it demonstrates: Real-time medical monitoring with alarm fatigue prevention.
Key Components:
- Sensors - HR, SpO2, Motion with TestCell ranges
- Clinical Logic - Severity mapping (page/warn/none)
- Duplicate Prevention - Distinct with ACK reset
- Paging - AsyncMapWithRetry for nurse notification
Architecture:
hrIn (TestCell 20-250) ─┐
spo2In (TestCell 0-100) ─┼─ publishVitals → Reading
motionIn ─────────────────┘
│
▼
vitalsIn
┌─────────┴─────────┐
▼ ▼
page gate warn gate
MapValue + Distinct MapValue + Distinct
+ Filter(page) + Filter(warn)
│ │
▼ ▼
pageHandle warnHandle
│
├─ observe → ledger PAGE
└─ AsyncMapWithRetry → pager
ackIn ─────────────────────────────────► resetDistinct()
Key Lessons:
- TestCell on sensor ingress, not on state
- Distinct before Filter (otherwise
nonenever arrives) - ACK resets Distinct on the same Receptor (not a new handle)
- AsyncMapWithRetry for I/O (not inside the policy chain)
- Motion → none (artifact wins)
22. Forensic Pipeline — Audit & Compliance
File: from_future_forensic_pipeline_demo.dart
What it demonstrates: Bridging legacy async APIs with full provenance tracking.
Key Components:
- Legacy API Bridge - deferFuture for per-request async
- Chain of Custody - Sequential Future steps
- Provenance - pulse.withStep() for forensic trace
- Error Handling - Forensic error tracking
- Audit Trail - Complete chain of custody
Architecture:
Legacy API (Future-based)
│
▼
deferFuture → fetchEvidence()
│
▼
deferFuture → fetchImages()
│
▼
deferFuture → fetchAuditLog()
│
▼
synthesis → Combined Report
│
▼
Observer → Audit Trail
Key Lessons:
- Use deferFuture for per-input async operations.
- Use fromFuture for one-shot async operations.
- Use asyncMapConcurrent for parallel operations.
- Chain deferFuture for sequential operations.
- Use synthesis to combine multiple sources.
- Handle errors in onError callback with forensic context.
- Preserve provenance with pulse.withStep().
23. Data Quality — Monitoring & Validation
File: filter_data_quality_demo.dart
What it demonstrates: Real-time data quality monitoring and filtering.
Key Components:
- Numeric Filtering - Range validation
- String Validation - Non-empty, pattern matching
- Complex Objects - User validation, business rules
- Sensor Data - Quality control, anomaly detection
- Multi-Condition - Pipeline filtering
- Log Filtering - Level-based filtering
Architecture:
Data Sources
│
▼
Flow.filter (Basic Validation)
│
▼
Flow.filter (Business Rules)
│
▼
Flow.map (Transformation)
│
▼
Flow.filter (Post-Validation)
│
▼
Observer → Results
Key Lessons:
- Use filter to remove invalid data from streams.
- Chain multiple filters for complex validation.
- Combine with map for filter-transform pipelines.
- Real-time filtering with fromStream.
- Great for: Data quality, monitoring, validation.
Operator Reference
Creation Operators
| Operator | Purpose | Use Case |
|---|---|---|
Cell.state |
Create state cell | Persistent state |
Cell.ingress |
Create input gateway | External events |
Flow.of |
Emit fixed sequence | Initial values |
Flow.fromIterable |
Emit from iterable | Static sequences |
Flow.range |
Emit numeric range | Counters, IDs |
Flow.repeat |
Emit repeated value | Heartbeats, signals |
Flow.fromFuture |
Bridge single Future | One-time loads |
Flow.deferFuture |
Bridge Future per input | Per-request loads |
Flow.fromStream |
Bridge Stream | Real-time data |
Flow.deferStream |
Bridge Stream per input | Dynamic streams |
Transformation Operators
| Operator | Purpose | Use Case |
|---|---|---|
Flow.map |
Transform each value | Data formatting |
Flow.mapTo |
Map to constant value | Signal conversion |
Flow.mapWithIndex |
Indexed transformation | Position tracking |
Flow.mapNotNull |
Drop null results | Optional values |
Flow.mapWhen |
Conditional mapping | Filter + transform |
Flow.mapValues |
Transform map values | Map transformation |
Flow.mapKeys |
Transform map keys | Key transformation |
Flow.pluck |
Extract field | Data extraction |
Flow.pluckOr |
Extract with default | Optional fields |
Flow.pluckAll |
Extract multiple fields | Data projection |
Flow.pluckPath |
Extract nested field | Deep navigation |
Async Operators
| Operator | Purpose | Use Case |
|---|---|---|
Flow.asyncMap |
Sequential async | Ordered tasks |
Flow.asyncMapConcurrent |
Parallel async | Throughput |
Flow.asyncMapLatest |
Latest only | Search-as-you-type |
Flow.asyncMapWithIndex |
Indexed async | Position tracking |
Flow.asyncMapWithRetry |
Retry on failure | Transient failures |
Flow.asyncMapWithTimeout |
Time-bound async | Deadlines |
Flow.asyncMapWithFallback |
Fallback on error | Graceful degradation |
Flow.asyncExpand |
Sequential flatten | Ordered sequences |
Flow.asyncExpandConcurrent |
Parallel flatten | Throughput |
Flow.asyncExpandLatest |
Latest flatten | Search |
Flow.asyncExpandExhaust |
Exhaust flatten | Submit protection |
Flow.asyncFold |
Seeded accumulation | Running totals |
Flow.asyncReduce |
Seedless accumulation | First value as seed |
Filter Operators
| Operator | Purpose | Use Case |
|---|---|---|
Flow.filter |
Remove by predicate | Validation |
Flow.take |
Take first N values | Sampling |
Flow.takeWhile |
Take while condition true | Conditional sampling |
Flow.takeUntil |
Take until event | Event-based stopping |
Flow.skip |
Skip first N values | Headers, warm-up |
Flow.skipWhile |
Skip while condition true | Conditional skipping |
Flow.skipUntil |
Skip until event | Event-based starting |
Flow.skipRepeated |
Skip consecutive duplicates | Noise reduction |
Flow.distinct |
Global deduplication | Unique items |
Flatten Operators
| Operator | Purpose | Use Case |
|---|---|---|
Flow.concatMap |
Sequential flatten | Ordered sequences |
Flow.concat |
Concatenate fixed inners | Static sequences |
Flow.concatAll |
Concatenate dynamic inners | Stream of streams |
Flow.mergeMap |
Concurrent flatten | Parallel processing |
Flow.switchMap |
Latest flatten | Dynamic sources |
Flow.exhaustMap |
Exhaust flatten | Submit protection |
Combine Operators
| Operator | Purpose | Use Case |
|---|---|---|
Flow.mergeWith |
Merge multiple sources | Event interleaving |
Flow.merge |
Merge with arming | Triggered merging |
Flow.mergeAll |
Flatten inner sequences | Stream of streams |
Flow.zipWith |
Pair by index | Synchronization |
Flow.zip |
Zip with arming | Triggered zipping |
Flow.zipAll |
Batch by count | Chunking |
Flow.combineLatestWith |
Latest from all sources | Real-time dashboards |
Flow.withLatestFrom |
Source-driven combination | Commands with state |
Flow.race |
First wins | Fastest response |
Time Operators
| Operator | Purpose | Use Case |
|---|---|---|
Flow.delay |
Fixed delay | Timing control |
Flow.delayWithSelector |
Payload-dependent delay | Adaptive timing |
Flow.delayWhen |
Notifier-based delay | External timing |
Flow.delayLatest |
Trailing delay | Latest after silence |
Flow.debounce |
Silence-based emission | Search, auto-save |
Flow.throttle |
Rate limiting | Scroll, clicks |
Flow.sample |
Sample on notifier | Event sampling |
Flow.sampleTime |
Time-based sampling | Periodic sampling |
Flow.audit |
Audit on notifier | Confirmation |
Flow.auditTime |
Time-based audit | Stabilization |
Flow.timeout |
Idle timeout | Inactivity detection |
Flow.timeoutWithFallback |
Timeout with |
Classes
- Flow Features Architecture Walkthroughs Demo · ICU Alarm Pipeline Demo · Pharmacy Transactions Demo · Search Stability Demo · Forensic Pipeline HowTo-FlowInstructions Method Chaining (Fluent)
- The Transcription Orchestrator for the Cell framework, responsible for converting (transcribing) external stimuli, persistent state, and logic into reactive Cell topographies.