Logic flows
Write reactive business logic with Effect.
In Logix, business logic is no longer scattered across component callbacks. Instead, it lives as Logic Flows. A Flow is essentially a reactive pipeline:
Event Source -> Transformation -> Effect Execution
Who is this for?
- You can already write simple
$.onAction / $.onStatelogic and want more advanced patterns (debounce + race handling + merging). - You want to use Logix to host long-running business processes such as search, polling, and batching.
Prerequisites
- You’ve read Flows & Effects and understand
run / runLatest / runExhaust. - You have a basic intuition for Effect and Stream (start with the Logic DSL / Flow API tabs; read Raw Effect only when needed).
What you’ll get
- Write production-grade logic: “watch state → debounce → async request → update state” using the Fluent DSL.
- Understand the mapping between Logic DSL / Flow API / Raw Effect, preparing you to build reusable Patterns.
Core APIs
The Bound API ($) is the entry point for building Flows:
$.onAction: watch Action dispatches$.onState: watch State changes$.flow: stream operators (e.g.debounce,filter) and execution strategies (e.g.run,runLatest)
Example: search autocomplete
This is a classic “watch state → debounce → handle races → async request” scenario.
import { Effect } from 'effect'
SearchModule.logic(($) =>
Effect.gen(function* () {
// 1) Watch keyword changes (Logic DSL)
// $.onState returns a Fluent Flow object you can chain directly.
yield* $.onState((s) => s.keyword).pipe(
$.flow.debounce(300), // debounce 300ms
$.flow.filter((kw) => kw.length > 2), // trigger only when length > 2
$.flow.runLatest((kw) =>
Effect.gen(function* () {
// runLatest handles races
const api = yield* $.use(SearchApi)
const results = yield* api.search(kw)
yield* $.state.update((s) => ({ ...s, results }))
}),
),
)
}),
)Concurrency strategies
Logix provides multiple execution strategies for concurrent events. They map directly to the FlowBuilder implementation:
| API | Semantics | Typical use cases |
|---|---|---|
run | Sequential | Default. Runs one Effect at a time; good for ordered operations. |
runParallel | Unbounded parallel | Run all events concurrently; good for independent logging/tracking. |
runLatest | Switch (latest wins) | Cancel in-flight Effect on new events; good for search/tab switch. |
runExhaust | Exhaust | Ignore new events while running; good for submit (prevent double-click). |
| (removed) |
State updates
In Flows, you typically update state with:
$.state.update(prev => next): pure functional update$.state.mutate(draft => ...): mutable Draft update (recommended; powered bymutative)
$.onAction('toggle').run(() =>
$.state.mutate((draft) => {
draft.isOpen = !draft.isOpen
}),
)Composition and reuse
Because Logic is an Effect under the hood, you can compose them easily:
const FeatureLogic = Effect.all([SearchLogic, PaginationLogic, FilterLogic])Next
- State management strategies: Managing state
- Lifecycle and watcher patterns: Lifecycle and watchers
- Cross-module collaboration: Cross-module communication