A high-performance state management core for models built on Effector. This implementation applies Harvard Architecture principles to reactive business logic, strictly separating Instructions (model definition) from Data (memory instances).
-
AOT (Ahead-of-Time) Analysis:
- The entire structure (
props,features,modes) is statically analyzed once during definition. - The engine generates a flat memory map (integer offsets) and an optimized
AccessorClassfor instant field retrieval.
- The entire structure (
-
Flat Memory Layout:
- A model instance is a single flat array (
Array(totalSize)). - All stores, events, and effects—even those deeply nested—are stored in this array.
- Field access is O(1) via direct array index lookups.
- A model instance is a single flat array (
-
Lifecycle Modes (RAII):
- Logic acts as a resource. It is "acquired" (mounted) when a mode is entered and "released" (unmounted) when left.
- Automatic Reset: When re-entering a mode, its state is guaranteed to be reset to defaults. No "stale state" issues.
- Dead Logic Elimination: Logic for inactive modes physically does not exist in the runtime graph (
clearNodeis called). We can even throw an error on dead unit access.
-
Strict Type Safety:
- Full TypeScript inference for complex fractal structures.
- Contextual Autocomplete:
override({ 'person.$name': ... })provides Intellisense for string paths. - Compile-time collision detection: Prevents naming conflicts between Props, Features, and Modes.
Models are treated as complex, composable devices:
context(Dependency Injection):
External dependencies that must be provided during creation (e.g., API clients, global configuration, history objects). Solves "prop drilling".props(Local Memory):
Basic primitives (define.store,define.event) that belong to this specific model.features(Fractal Composition):
Nested models embedded within the parent. Physically, their memory is "squashed" into the parent's flat array, but logical access is hierarchical.modes(Operational States):
Mutually exclusive operational contexts (e.g.,Flightvs.Ground).impl(The Circuit Board):
The wiring function. It receives a Flattened Scope (Props + Features + Modes) and an Environment (Context).
import { createStore, createEffect, sample } from 'effector';
import { model, define, create, redefine, override } from '@effector/model';
// EXTERNAL DEPENDENCIES (Simulated Context)
const loggerFx = createEffect(async (msg: string) => {
console.log(`[Cockpit Radio]: ${msg}`);
});
// ============================================================================
// 1. STATIC FEATURES (Reusable Components)
// ============================================================================
const PersonFeature = model({
props: {
$name: define.store('Anonymous'),
$wallet: define.store(0),
employer: define.val('Unknown'),
},
});
const PilotFeature = model({
props: {
$totalFlights: define.store(0),
incFlights: define.event(),
},
impl: (scope) => {
sample({
clock: scope.incFlights,
source: scope.$totalFlights,
fn: (total) => total + 1,
target: scope.$totalFlights,
});
},
});
// ============================================================================
// 2. DYNAMIC MODES (State Machines)
// ============================================================================
const FlightMode = model({
props: {
$altitude: define.store(0),
land: define.event<void>(), // Landing only possible here
},
impl: (scope) => {
return {
// Export computed state
$isAtCruise: scope.$altitude.map((a) => a > 10000),
};
},
});
const GroundMode = model({
props: {
$fuelLevel: define.store(100),
refuel: define.event<number>(),
takeOff: define.event<void>(), // Takeoff only possible from ground
getPaid: define.event<void>(),
},
impl: (scope) => {
sample({
clock: scope.refuel,
source: scope.$fuelLevel,
fn: (level, add) => Math.min(level + add, 100),
target: scope.$fuelLevel,
});
},
});
// ============================================================================
// 3. MAIN MODEL (The Boeing 747)
// ============================================================================
const BoeingPilot = model({
// Dependencies required by this model
context: {
radioFx: define.effect<string, void>(),
$flyRate: define.store(500),
},
// Local State
props: {
$isFlying: define.store(false),
reportStatus: define.event<void>(),
},
// Composition
features: {
// Modify definition defaults via redefine()
person: redefine(
PersonFeature,
override({
employer: define.val('Boeing'),
}),
),
pilot: PilotFeature,
},
// Topology Switching
modes: {
operation: {
source: (scope) => scope.$isFlying,
// Automatic switching based on source store value
match: (isFlying) => (isFlying ? 'flight' : 'ground'),
cases: {
flight: FlightMode,
ground: GroundMode,
},
},
},
// Logic Wiring (Scope + Context)
impl: (scope, { context }) => {
// Internal variable (not exposed on API)
const $pendingPayout = createStore(0);
// --- Mode Switching Triggers ---
// Takeoff (only works in Ground Mode)
sample({
clock: scope.operation.ground.takeOff,
fn: () => true,
target: [scope.$isFlying, scope.pilot.incFlights],
});
// Land (only works in Flight Mode)
sample({
clock: scope.operation.flight.land,
fn: () => false,
target: scope.$isFlying,
});
// --- Business Logic ---
// Accrue pay per flight
sample({
clock: scope.pilot.incFlights,
source: { current: $pendingPayout, rate: context.$flyRate },
fn: ({ current, rate }) => current + rate,
target: $pendingPayout,
});
// Payout (Only possible on Ground)
sample({
clock: scope.operation.ground.getPaid,
source: { wallet: scope.person.$wallet, accrued: $pendingPayout },
fn: ({ wallet, accrued }) => wallet + accrued,
target: scope.person.$wallet,
});
// Reset internal debt counter after payout
sample({
clock: scope.operation.ground.getPaid,
target: ($pendingPayout as any).reinit,
});
// --- Reporting (Using Injected Context) ---
sample({
clock: scope.reportStatus,
source: {
isFlying: scope.$isFlying,
name: scope.person.$name,
payout: $pendingPayout,
// Safe access to mode state: returns default value if mode inactive
altitude: scope.operation.flight.$altitude,
fuelLevel: scope.operation.ground.$fuelLevel,
},
fn: (state) => {
const status = state.isFlying ? `In Flight at ${state.altitude}m` : `On Ground (Fuel: ${state.fuelLevel}%)`;
return `Report for ${state.name} (${scope.person.employer}): ${status}. Pending Pay: ${state.payout}$`;
},
target: context.radioFx,
});
return {
$pendingPayout,
};
},
});
// ============================================================================
// 4. INSTANTIATION (Runtime)
// ============================================================================
const captainPilot = create({
model: BoeingPilot,
// 1. Inject Context (DI)
context: {
radioFx: loggerFx,
$flyRate: createStore(1500), // Override default rate
},
// 2. Override Props (Path-based, Typesafe)
props: override({
'person.$name': createStore('Captain Maverick'),
// Note: No 'features.' prefix needed, path matches the scope structure
}),
});
// --- SCENARIO ---
// 1. On Ground. Check defaults.
captainPilot.reportStatus();
// > [Cockpit Radio]: Report for Captain Maverick (Boeing): On Ground (Fuel: 100%). Pending Pay: 0$
// 2. Refuel (Works)
captainPilot.operation.ground.refuel(-20);
// 3. Takeoff
captainPilot.operation.ground.takeOff();
// 4. Airborne behavior
// Try to refuel? Ignored. Ground logic is unmounted.
captainPilot.operation.ground.refuel(50);
// Update flight state (Typescript prevents access to .state directly, must use events or internal logic)
// For demo, we simulate internal update:
// ... (altitude changes to 12000) ...
captainPilot.reportStatus();
// > [Cockpit Radio]: Report for Captain Maverick (Boeing): In Flight at 12000m. Pending Pay: 1500$
// 5. Land
captainPilot.operation.flight.land();
// 6. Get Paid
captainPilot.operation.ground.getPaid();