Triggers
Transform Data Changes to FSM Events
Triggers serve as reactive event generators that bridge application data and state machine transitions within StateWalker’s unidirectional data flow architecture. They observe data models and convert data changes into events that drive state transitions, representing the sole connection point between the generic core library and the orchestrator framework.
Triggers access models through the process context using the adapter pattern. This approach maintains loose coupling while enabling reactive connections between the data layer and state machine events. Unlike controllers that modify models, triggers maintain strict read-only access to application data.
Triggers establish subscriptions to models through onChange
listeners, enabling them to respond automatically to data changes by evaluating business rules and generating appropriate events. When models change, triggers analyze these modifications and yield string events that the state machine orchestrator uses to determine and execute state transitions.
Triggers return cleanup functions through async generator patterns to ensure proper resource management during state transitions. These cleanup functions unsubscribe from model change notifications and release any allocated resources, preventing memory leaks and maintaining system stability.
Implementation Examples
Basic Trigger: Model Access and Event Generation
import { newAsyncGenerator } from "@/utils/generators";
export default async function* UserLoginTrigger(context: ProcessContext): AsyncGenerator<string> {
// Context access: Get model through process context
const userModel = getUserModel(context);
// Event generation: Transform model changes into FSM events
yield* newAsyncGenerator((dispatch) => {
// Model subscription: Listen to changes in read-only mode
return userModel.onChange((user) => {
if (user.isAuthenticated) {
dispatch("loginSuccess");
} else {
dispatch("logout");
}
});
});
}
Advanced Trigger: Multiple Model Monitoring and Complex Cleanup
async function* AuthenticationTrigger(context: ProcessContext) {
// Registry pattern: Manage multiple cleanup functions
const [register, cleanup] = newRegistry();
// Context access: Get multiple models
const userModel = getUserModel(context);
const authModel = getAuthModel(context);
yield* newAsyncGenerator((dispatch) => {
// Business logic: Evaluate authentication state from multiple models
const checkAuthenticationStatus = () => {
if (authModel.isAuthenticated && userModel.profile) {
dispatch("authenticated");
} else if (authModel.authError) {
dispatch("authenticationFailed");
} else if (authModel.sessionExpired) {
dispatch("sessionExpired");
}
};
// Model subscriptions: Monitor changes across multiple models
register(userModel.onChange(checkAuthenticationStatus));
register(authModel.onChange(checkAuthenticationStatus));
// Initial evaluation on trigger activation
checkAuthenticationStatus();
// Cleanup function: Registry releases all subscriptions
return cleanup;
});
}
Complex Event Stream: Resource Management and Multiple Subscriptions
async function* UserActivityTrigger(context: ProcessContext): AsyncGenerator<string> {
// Context access: Get models for activity monitoring
const userModel = getUserModel(context);
const sessionModel = getSessionModel(context);
yield* newAsyncGenerator<string>((next, done) => {
// Model subscription: Monitor user state changes
const userCleanup = userModel.onChange(() => {
const user = userModel.currentUser;
// Event generation: Transform user state into activity events
if (!user) {
next("userLoggedOut");
} else if (user.isActive) {
next("userBecameActive");
} else {
next("userBecameInactive");
}
});
// Model subscription: Monitor session state changes
const sessionCleanup = sessionModel.onChange(() => {
if (sessionModel.isExpired) {
next("sessionExpired");
}
});
// Cleanup function: Release all model subscriptions
return () => {
userCleanup();
sessionCleanup();
};
});
}
Key Concepts in Practice
Event Generation: Triggers observe model changes and transform them into string events within the unidirectional data flow architecture. This enables declarative state management that responds to data changes rather than requiring manual event coordination.
Context Access: The process context provides access to models using the adapter pattern. This maintains loose coupling while enabling triggers to monitor application data in read-only mode.
Model Monitoring: Triggers establish reactive subscriptions to models through onChange
listeners, evaluating business rules to determine when state transitions should occur.
Resource Management: Triggers use async generator patterns with cleanup functions to unsubscribe from model notifications and release resources. The registry pattern manages multiple cleanup operations automatically.
These patterns combine to create triggers that respond to model changes, generate state machine events, access shared resources through context, and maintain clean resource management throughout the application lifecycle.