Scheduler Models
1. Introduction and Background
1.1 Wrkbelt Platform Overview
Wrkbelt is a comprehensive suite of digital tools designed to empower service businesses with operational efficiency. The platform addresses the challenges of managing customer interactions, scheduling, and service delivery through intuitive digital interfaces and seamless integrations with industry-standard services like ServiceTitan.
1.2 The Scheduler Tool
The Wrkbelt Scheduler is a core component that enables service businesses to create customizable booking experiences for their customers. It provides a step-based approach to gathering information, scheduling appointments, and capturing customer details through an intuitive, conversation-like interface.
Each booking flow consists of a sequence of steps that may include:
- Zip code validation (to confirm service area)
- Service selection (to determine what service the customer needs)
- Detail collection (to gather context about the service request)
- Calendar selection (to schedule the appointment)
- Customer information collection (to identify new or existing customers)
- Summary and confirmation (to review and finalize the booking)
These steps guide customers through a structured process that culminates in a confirmed service appointment, which is then synchronized with the business's ServiceTitan account.
1.3 Evolution of the Scheduler
The Scheduler has evolved to address the need for more natural, conversational booking experiences. Rather than treating service selection and information gathering as strictly sequential steps, our refined model offers a graph-based approach that combines these elements into intuitive decision trees. This allows businesses to create dynamic flows that adapt based on customer inputs while ensuring every path identifies exactly one service type to be booked.
2. Scheduler Data Model
2.1 Architecture Overview
The Scheduler data model is built on a modular architecture with distinct but interconnected entities:
- Configuration Layer - Defines how the scheduler appears and behaves in different contexts
- Flow Structure Layer - Specifies the sequence and behavior of booking flow steps
- Content Layer - Provides the reusable content elements like questions, answers, and services
- Session Layer - Tracks and records customer interactions with booking flows using an event-sourcing approach
This layered approach creates a flexible system that can be customized to meet the needs of different service businesses while maintaining consistent core functionality.
2.2 Core Concepts
2.2.1 Dynamic Flow Routing
The Scheduler implements a sophisticated URL-based routing system using the BookingFlowRouter entity. This enables organizations to dynamically direct users to different booking flows based on various parameters:
- URL Path Matching: Route users based on specific URL paths (e.g.,
/services/hvacvs/services/plumbing) - Query Parameter Matching: Route based on query string parameters (e.g.,
?location=downtown) - Campaign Tracking: Route based on UTM parameters for campaign-specific experiences
- Priority-Based Resolution: Resolve conflicts using a strict priority hierarchy
The routing system follows a rules engine architecture that encapsulates matching logic and provides validation tools to prevent routing conflicts. This allows organizations to implement sophisticated traffic management strategies without code changes.
2.2.2 Graph-Based Structure for Service Selection
The Service Selection step is modeled as a directed acyclic graph (DAG) with the following key elements:
- Questions: Standalone entities that present information requests to users
- Answers: Possible responses to questions that guide the flow direction
- Services: Specific service offerings that map to ServiceTitan job types
- Answer Categories: Grouping mechanisms for organizing related answers or services
The graph always begins with a single root question and branches based on user selections, with every possible path including exactly one service selection.
2.2.2 Path Requirements
For a valid Service Selection graph:
- There must be exactly one root question node as the starting point
- Every possible path through the graph must include exactly one service type node
- A service type can appear anywhere in the path (not just at the end)
- Paths may continue with additional questions after service selection
- Terminal nodes (those with no outgoing connections) conceptually connect to the next configured step in the booking flow
2.2.3 Composable Configuration
The Scheduler's design embraces composability through:
- SchedulerConfig: Links organization, booking flow, and branding into a unified experience
- ContentConfig Pattern: Provides consistent structure for text and visual elements
- BrandKit: Defines reusable visual styling that can be applied across scheduler instances
2.2.4 Event-Sourcing Architecture for Session Tracking
The enhanced session tracking system follows an event-sourcing approach with:
- Events as Source of Truth: All user interactions captured as immutable events
- State Derivation: Current session state derived from the event stream
- Discriminated Unions: Type-safe access to status-specific fields based on session status
- Enhanced Step Tracking: Detailed tracking of step progression and drop-off points
3. Scheduler Entity Relationship Diagram
4. Entity Descriptions
4.1 Configuration Entities
4.1.1 SchedulerConfig
The SchedulerConfig entity is the central configuration component that ties together initialization settings for our scheduler. This entity is the main entry point for fetching a booking when the bundled scheduler initially loads. The resulting response will include the SchedulerConfig entity enriched with the appropriate BookingFlow (given the routing context) as well as the respective Organization's BrandKit.
Key fields:
target_click_elements: Array of selectors for elements that trigger the schedulerfallback_phone_number_screen: Optional configuration for when online booking is unavailablephone_number: Alternative contact method for customerscontent: Instructions and messaging for the fallback screen
This entity empowers organizations to create multiple scheduler configurations for different contexts (e.g., website booking, mobile app, embedded widget) while reusing the same underlying booking flows and service definitions.
4.1.2 BrandKit
The BrandKit entity defines the visual styling for a scheduler instance. It enables consistent branding across an organization's digital presence.
Key fields:
logo_file_id: References the organization's logo imagecolors: Defines the color scheme for the scheduler interfaceprimary: Main brand color used for key UI elementssecondary: Supporting color for additional UI componentsaccent: Highlight color for important actions and elements
4.2 Booking Flow Entities
4.2.1 BookingFlow
The BookingFlow entity represents a complete booking experience consisting of multiple steps in a defined sequence.
Key fields:
content: Contains the flow's title and other display informationsteps: Array of references to BookingFlowStep entities in sequence orderstatus: Enumerates the flow's publication state (draft, published, deleted)
A booking flow is the central element of the scheduler that orchestrates the customer journey from initial interaction to completed booking.
4.2.2 BookingFlowStep
The BookingFlowStep entity defines an individual step within a booking flow. Each step has a specific type that determines its behavior and configuration.
Key fields:
step_type: Identifies the step's purpose and behavior (e.g., zipcode, service_selection, details)step_config: Contains display information like title and descriptiontype_config: Type-specific configuration based on the step_type value
Step types include:
- Zipcode: Validates if a customer is within the service area
- Service Selection: Implements the graph-based decision tree for service selection
- Details: Collects additional information about the service request
- Timeslot Selection: Allows scheduling of the service appointment
- Customer: Captures customer contact information
- Summary: Reviews all collected information before confirmation
For service selection steps, the type_config includes:
root_node_id: Identifies the starting question in the graphnodes: Array of question, answer, and service nodesedges: Array of connections between nodes that define possible paths
4.3 Content Entities
4.3.1 BookingService
The BookingService entity defines a specific service offering that can be booked by customers. It includes both customer-facing content and vendor integration details.
Key fields:
service_content: Display information like title, description, and iconvendor_data: Integration information for connecting to external systemsvendor_type: Identifies the integration provider (e.g., servicetitan)job_type_id: External identifier for the service in the vendor systemjob_type_name: Human-readable name from the vendor systemvendor_sync_status: Status of synchronization with the vendor system
This entity bridges the gap between customer-facing service offerings and the underlying service management systems used by the business.
4.3.2 BookingQuestion
The BookingQuestion entity defines a reusable question that can be presented to customers during the booking process.
Key fields:
question_content: Display information including title, unique name, and help textanswers: Array of possible answers to the question
Questions are fundamental building blocks in the service selection graph, representing decision points in the customer journey.
4.3.3 BookingQuestionAnswer
The BookingQuestionAnswer entity represents a possible response to a booking question. Answers can either reference a service or provide plain text content.
Key fields:
type: Distinguishes between service references and plain text answerscategory_id: Optional reference to a category for grouping related answersservice_id: Reference to a service (required for service_reference type)content: Display information for plain text answers
The dual nature of answers (service or plain text) enables the graph to both guide service selection and gather contextual information in a unified flow.
4.3.4 BookingAnswerCategory
The BookingAnswerCategory entity provides a way to group related answers for better organization and presentation.
Key fields:
title: Display name for the categorydescription: Detailed explanation of the category's purposeicon: Visual identifier for the category
Categories help create logical groupings of answers, improving the user experience by organizing options in meaningful ways.
4.4 Session Tracking Entities
4.4.1 BookingSession
The BookingSession entity records a customer's interaction with a booking flow using an event-sourcing approach combined with state management to provide comprehensive journey tracking. The model is structured to provide both operational access to current state and analytical insights through outcome tracking.
Key structural components:
-
Core Identification and References
organization_id: References the parent organizationbooking_flow_id: References the booking flow being usedbooking_session_event_ids: Chronological array of references to event entities
-
Context and Analytics Data
session_context: Marketing and user context data (UTM parameters, device info, etc.)step_progression: Tracking statistics for step completion and furthest progressmetrics: Session metrics like duration and navigation patterns
-
Current State Management
current_state: Container for active session operational datacurrent_state_step_id: Current active stepcurrent_state_form_data: Current form valuescurrent_state_navigation_state: Navigation status informationcurrent_state_service_selection_path: Path taken through service selectioncurrent_state_last_event_id: Reference to the most recent eventcurrent_state_temp_selections: Temporary selections (service, timeslot, etc.)current_state_last_activity_at: Timestamp of most recent activity
-
Outcome Tracking
booking_outcome: Container for session outcome databooking_outcome_status: Session status (active, completed, abandoned)- Status-specific fields depending on outcome:
- For completed sessions:
booking_outcome_completed_at,booking_outcome_booking_id,booking_outcome_selected_service_id,booking_outcome_selected_timeslot - For abandoned sessions:
booking_outcome_abandoned_at,booking_outcome_drop_off_step_id
- For completed sessions:
This structured approach provides clear separation of concerns between operational state and outcome tracking, while enabling comprehensive analysis of user journeys through the booking process.
4.4.2 BookingSessionEvent
The BookingSessionEvent entity implements an event-sourcing pattern to capture all interactions within a booking session as immutable events. These events collectively form the complete history of a booking session and can be used to reconstruct its state at any point in time.
Key fields:
booking_session_id: References the parent booking sessionevent_type: Identifies the nature of the interactiontimestamp: When the event occurredstep_id: The step context where the event took placedata: Event-specific data structured as a discriminated union based on event_type
Event types include:
SESSION_STARTED: Initial creation of the booking sessionSTEP_ENTERED: User navigated to a specific stepSTEP_COMPLETED: User successfully completed a stepFIELD_UPDATED: Form field value was changedVALIDATION_ERROR: Form validation failedQUESTION_ANSWERED: Response provided to a service selection questionSERVICE_SELECTED: Service was chosen by the userNAVIGATION_BACK: User navigated backwards in the flowTIMESLOT_SELECTED: Appointment time was selectedSESSION_ABANDONED: User prematurely ended the sessionSESSION_COMPLETED: Booking was successfully finalized
This event-based approach provides several benefits:
- Complete auditability of the user journey
- Ability to reconstruct session state at any point
- Detailed insights into user behavior and drop-off points
- Support for session recovery in case of disconnection
- Rich analytics capabilities for optimization
5. Data Modeling Patterns
5.1 ContentConfig Pattern
The Scheduler data model uses a consistent ContentConfig pattern for display-related information across multiple entities:
type ContentConfig = {
title: string;
description?: string;
icon?: LucideIconKey;
};
This pattern appears in various forms throughout the model:
service_contentin BookingServicequestion_contentin BookingQuestionstep_configin BookingFlowStepcontentin fallback phone number screen
Using this consistent pattern simplifies the implementation of UI components and ensures uniform presentation across the application.
5.2 Discriminated Union Types
The model employs discriminated unions to handle entities with multiple possible structures based on a type field. For the BookingSession entity, this is implemented through the booking_outcome_status field:
// For BookingSession based on booking_outcome_status
export enum BookingSessionStatus {
ACTIVE = 'active',
COMPLETED = 'completed',
ABANDONED = 'abandoned'
}
// Base fields for all session types
export interface BookingSessionBase {
_id: string;
organization_id: string;
booking_flow_id: string;
booking_session_event_ids: string[];
session_context: SessionContext;
step_progression: StepProgression;
metrics: SessionMetrics;
current_state: CurrentState;
booking_outcome: {
status: BookingSessionStatus;
// Additional fields based on status
};
// ...other common fields
}
// Active session type
export interface ActiveBookingSession extends BookingSessionBase {
booking_outcome: {
status: BookingSessionStatus.ACTIVE;
};
}
// Completed session type with additional fields
export interface CompletedBookingSession extends BookingSessionBase {
booking_outcome: {
status: BookingSessionStatus.COMPLETED;
completed_at: Date;
booking_id: string;
selected_service_id: string;
selected_timeslot: {
start_time: Date;
end_time: Date;
};
};
}
// Abandoned session type with abandonment-specific fields
export interface AbandonedBookingSession extends BookingSessionBase {
booking_outcome: {
status: BookingSessionStatus.ABANDONED;
abandoned_at: Date;
drop_off_step_id: string;
// Optional - these might have been selected before abandonment
selected_service_id?: string;
selected_timeslot?: {
start_time: Date;
end_time: Date;
};
};
}
// Union type
export type BookingSession =
| ActiveBookingSession
| CompletedBookingSession
| AbandonedBookingSession;
Similarly, for BookingSessionEvent data:
export enum BookingSessionEventType {
SESSION_STARTED = 'session_started',
STEP_ENTERED = 'step_entered',
// ...other event types
}
// Base event structure
export interface BookingSessionEvent {
_id: string;
booking_session_id: string;
organization_id: string;
event_type: BookingSessionEventType;
timestamp: Date;
step_id: string;
data: BookingSessionEventData;
}
// Discriminated union for event data
export type BookingSessionEventData =
| StepNavigationEventData
| FieldUpdateEventData
| QuestionAnswerEventData
// ...other event data types
This pattern enables type-safe handling of different entity variants while maintaining a consistent base structure, providing both flexibility and compile-time type checking.
5.3 Type Guards for Safe Access
The model includes type guard functions to provide type-safe access to status-specific fields:
export function isActiveSession(session: BookingSession): session is ActiveBookingSession {
return session.booking_outcome.status === BookingSessionStatus.ACTIVE;
}
export function isCompletedSession(session: BookingSession): session is CompletedBookingSession {
return session.booking_outcome.status === BookingSessionStatus.COMPLETED;
}
export function isAbandonedSession(session: BookingSession): session is AbandonedBookingSession {
return session.booking_outcome.status === BookingSessionStatus.ABANDONED;
}
These type guards enable safe access to status-specific fields with proper TypeScript type narrowing:
function processSession(session: BookingSession) {
if (isCompletedSession(session)) {
// TypeScript knows this is a CompletedBookingSession
console.log(`Booking completed at ${session.booking_outcome.completed_at} with ID ${session.booking_outcome.booking_id}`);
} else if (isAbandonedSession(session)) {
// TypeScript knows this is an AbandonedBookingSession
console.log(`Booking abandoned at ${session.booking_outcome.abandoned_at} during step ${session.booking_outcome.drop_off_step_id}`);
} else {
// TypeScript knows this is an ActiveBookingSession
console.log(`Booking active with current step ${session.current_state.step_id}`);
}
}
5.4 Array-Based Event References
The BookingSession entity uses an array of ObjectId references (booking_session_event_ids) to track all related events in chronological order:
interface BookingSessionBase {
// ...other fields
booking_session_event_ids: string[]; // Array of BookingSessionEvent _id values
}
This approach provides several benefits:
- Efficient retrieval of chronological events
- Direct access to specific events by ID
- Ability to track event sequence without complex queries
- Avoids embedding a potentially large array of events directly in the session document
5.5 Step-Specific Configuration
The BookingFlowStep entity continues to use type-specific configuration based on the step_type:
export type BookingFlowServiceSelectionStep = BookingFlowStepBase & {
step_type: StepType.SERVICE_SELECTION;
type_config: ServiceSelectionTypeStepConfig;
};
export type BookingFlowZipcodeStep = BookingFlowStepBase & {
step_type: StepType.ZIPCODE;
type_config: ZipcodeTypeStepConfig;
};
This approach enables each step type to have its own specialized configuration while sharing common base fields, creating a flexible but structured modeling pattern.
6. Service Selection Implementation
6.1 Graph Structure
The service selection step employs a graph structure defined by nodes and edges:
Node Types
type ServiceSelectionNode = {
_id: string;
type: 'question_reference' | 'answer_reference' | 'service_reference';
// For question references
question_id?: string;
// For answer nodes
answer_content?: {
text: string;
description?: string;
};
answer_display?: {
icon?: string;
style?: string;
};
answer_category_id?: string;
// For service references
service_id?: string;
};
Edge Definition
type ServiceSelectionEdge = {
_id: string;
source_node_id: string;
target_node_id: string;
};
6.2 Path Resolution
When a customer interacts with a service selection step, the system:
- Starts from the root question node
- Presents the question with its possible answers
- When an answer is selected:
- If the answer is a service reference, records the service selection
- If the answer leads to another question, continues the flow
- Tracks the full path taken through the graph via BookingSessionEvents
- Ensures exactly one service is selected before proceeding to the next step
This approach creates an adaptive, conversation-like experience while maintaining structured data capture for business operations.
7. Event-Sourcing Architecture
7.1 Core Principles
The enhanced session tracking system follows an event-sourcing architecture with these core principles:
- Events as Source of Truth: All user interactions are captured as immutable events in the BookingSessionEvent collection
- State Derivation: Current session state is derived from the event stream
- Array-Based Event References: Sessions maintain an ordered array of event references for efficient retrieval
- Separation of Concerns: Operational data (current state) is separated from historical/analytical data (event history)
- Enhanced Step Tracking: Detailed tracking of step progression and drop-off points
- Status-Based Type Discrimination: Proper use of discriminated unions based on session status
7.2 Event Processing Flow
The session tracking system processes events in the following sequence:
- Client application emits events representing user interactions
- Session service processes the event
- Stores the event in the BookingSessionEvent collection
- Updates the BookingSession document with a reference to the event
- Updates the current state snapshot based on the event
- For status transitions (completing or abandoning a session):
- Creates the appropriate status transition event
- Transforms the session to the appropriate status type with status-specific fields
- Ensures all temporary selections are properly stored in the appropriate fields
7.3 Temporary Selection Management
During an active session, selections that haven't been committed to an outcome are tracked in the current_state_temp_selections field:
interface ActiveBookingSession extends BookingSessionBase {
booking_outcome: {
status: BookingSessionStatus.ACTIVE;
};
current_state: {
// ...other fields
temp_selections?: {
service_id?: string;
timeslot?: {
start_time: Date;
end_time: Date;
}
}
};
}
When a session transitions to completed or abandoned status, these temporary selections are moved to the appropriate outcome-specific fields and the temporary storage is removed:
// When transitioning from active to completed
function completeSession(session: ActiveBookingSession, bookingId: string): CompletedBookingSession {
return {
...session,
booking_outcome: {
status: BookingSessionStatus.COMPLETED,
completed_at: new Date(),
booking_id: bookingId,
selected_service_id: session.current_state.temp_selections?.service_id || '',
selected_timeslot: session.current_state.temp_selections?.timeslot || {
start_time: new Date(),
end_time: new Date()
}
},
current_state: {
...session.current_state,
// Remove temp_selections
temp_selections: undefined
}
};
}
7.4 Event Reference Management
To prevent issues with MongoDB's document size limits, the booking_session_event_ids array is managed in one of two ways:
- Rolling Window: Keeping only the most recent N events while maintaining the first event for context
- Significant Events: Storing only significant events that are essential for reconstructing the session
This approach ensures that session documents remain within MongoDB's size limits while preserving the most important events for analysis and state reconstruction.
8. Analytics Capabilities
8.1 Step-Based Abandonment Analysis
The enhanced session tracking model enables detailed analysis of where users abandon the booking process:
// Get step-specific abandonment rates
async function getStepAbandonmentRates(organizationId, dateRange) {
return await bookingSessionRepository.aggregate([
// Match abandoned sessions for the given organization and date range
{ $match: {
organization_id: organizationId,
created_at: { $gte: dateRange.start, $lte: dateRange.end },
'booking_outcome.status': BookingSessionStatus.ABANDONED
}},
// Group by drop-off step
{ $group: {
_id: '$booking_outcome.drop_off_step_id',
count: { $sum: 1 }
}},
// Lookup step details
{ $lookup: {
from: 'booking_flow_steps',
localField: '_id',
foreignField: '_id',
as: 'step_info'
}},
// Format results
{ $project: {
step_id: '$_id',
step_name: '$step_info.step_config.title',
step_type: '$step_info.step_type',
abandonment_count: '$count'
}}
]);
}
8.2 Progression Analysis
The step_progression field enables analysis of how far users progress through booking flows:
// Get completion metrics by steps completed
async function getProgressionMetrics(organizationId, dateRange) {
return await bookingSessionRepository.aggregate([
// Match sessions for the given organization and date range
{ $match: {
organization_id: organizationId,
created_at: { $gte: dateRange.start, $lte: dateRange.end }
}},
// Group by status and steps completed
{ $group: {
_id: {
status: '$booking_outcome.status',
steps_completed: '$step_progression.completed_count',
total_steps: '$step_progression.total_in_flow'
},
count: { $sum: 1 }
}},
// Calculate completion percentage
{ $project: {
status: '$_id.status',
steps_completed: '$_id.steps_completed',
total_steps: '$_id.total_steps',
completion_percentage: {
$multiply: [
{ $divide: ['$_id.steps_completed', '$_id.total_steps'] },
100
]
},
count: 1
}}
]);
}
8.3 Event Analysis
The event-sourcing architecture enables detailed analysis of user interactions:
// Get event frequency analysis
async function getEventFrequencyAnalysis(bookingSessionId) {
// Get the session to access event IDs
const session = await bookingSessionRepository.findById(bookingSessionId);
// Get all events for this session
const events = await bookingSessionEventRepository.find({
_id: { $in: session.booking_session_event_ids }
}, { sort: { timestamp: 1 } });
// Analyze event frequency
const eventCounts = {};
events.forEach(event => {
eventCounts[event.event_type] = (eventCounts[event.event_type] || 0) + 1;
});
// Calculate time between events
const eventTimings = [];
for (let i = 1; i < events.length; i++) {
const timeDiff = (events[i].timestamp - events[i-1].timestamp) / 1000; // in seconds
eventTimings.push({
event_type: events[i].event_type,
time_since_previous: timeDiff
});
}
return {
event_counts: eventCounts,
event_timings: eventTimings,
total_events: events.length
};
}
9. Performance Optimization
9.1 Database Indexing
Critical indexes for the enhanced data model:
// BookingSession collection indexes
db.booking_sessions.createIndex({ organization_id: 1, 'booking_outcome.status': 1, created_at: -1 });
db.booking_sessions.createIndex({ 'booking_outcome.booking_id': 1 }, { sparse: true });
db.booking_sessions.createIndex({ 'booking_outcome.status': 1, 'booking_outcome.drop_off_step_id': 1 });
// BookingSessionEvent collection indexes
db.booking_session_events.createIndex({ booking_session_id: 1, timestamp: 1 });
db.booking_session_events.createIndex({ organization_id: 1, event_type: 1, timestamp: 1 });
9.2 Array Size Management
To prevent the booking_session_event_ids array from causing document size issues, we implement strategies such as:
- Monitoring array size distribution across sessions
- Implementing a rolling window to keep only the most recent events
- Selectively storing only significant events
- Applying TTL index to automatically clean up old events
9.3 Batched Updates
For high-traffic scenarios, batch processing is implemented to reduce database write operations:
// Event batch processor for high-volume scenarios
class EventBatchProcessor {
private eventQueue: {event: any, sessionId: string}[] = [];
queueEvent(event: any, sessionId: string) {
this.eventQueue.push({ event, sessionId });
// Process immediately if queue gets large
if (this.eventQueue.length >= 100) {
this.processBatch();
}
}
async processBatch() {
if (this.eventQueue.length === 0) return;
const batch = [...this.eventQueue];
this.eventQueue = [];
try {
// Group by session ID to minimize session document updates
const sessionGroups = new Map<string, {events: any[], sessionId: string}>();
batch.forEach(({ event, sessionId }) => {
if (!sessionGroups.has(sessionId)) {
sessionGroups.set(sessionId, { events: [], sessionId });
}
sessionGroups.get(sessionId)!.events.push(event);
});
// Process each session group
for (const [_, { events, sessionId }] of sessionGroups.entries()) {
// Insert all events in bulk
const savedEvents = await this.eventRepository.insertMany(events);
// Update session with event IDs in a single operation
const eventIds = savedEvents.map(e => e._id);
await this.sessionRepository.updateOne(
{ _id: sessionId },
{
$push: {
booking_session_event_ids: { $each: eventIds }
},
$set: {
'current_state.last_activity_at': new Date()
}
}
);
}
} catch (error) {
console.error('Error processing event batch:', error);
// Add events back to queue
this.eventQueue = [...batch, ...this.eventQueue];
}
}
}
10. Conclusion
The Wrkbelt Scheduler data model represents a sophisticated approach to online service booking that balances flexibility with structure. The event-sourcing architecture for session tracking provides comprehensive insights into user journeys while maintaining scalable performance.
Key strengths of the model include:
- Comprehensive Journey Tracking: Complete visibility into user paths through event sourcing
- Structured State Management: Clear separation between current state and outcome data
- Type-Safe State Transitions: Discriminated unions ensure proper state handling and type safety
- Detailed Abandonment Analysis: Precise understanding of where and why users drop off
- Performance Optimization: Efficient data structures and processing patterns for scale
- Rich Analytics Capabilities: Detailed insights for conversion optimization
- Alignment with Existing Patterns: Follows established conventions in the codebase
This robust data model forms the foundation of a scheduler tool that bridges the gap between intuitive customer experiences and robust backend business operations, with particular emphasis on understanding and optimizing the user journey through detailed session tracking.