Skip to main content

Booking Flows

The Booking Flow entity manages the structured configuration of booking experiences within the scheduling system. Each booking flow represents a defined sequence of steps that guides users through the booking process, with validation to ensure the correct ordering of steps and data integrity. Booking flows are organization-specific and can be created, published, and managed through a RESTful API interface.

Schema Definition

type BookingFlow = BaseEntityWithOrganization & {
// Base Properties
_id: string; // MongoDB ObjectId
organization_id: string; // Required organization reference
content: { // Required content configuration
title: string; // Required flow title (1-100 chars)
};
steps: string[]; // Array of BookingFlowStep references
status: BookingFlowPublishedStatus; // Publication status

// Timestamps
createdAt: Date; // Auto-generated
updatedAt: Date; // Auto-updated
};

Field Descriptions

Base Properties

FieldTypeRequiredUniqueDescription
_idObjectIdYesYesUnique identifier for the booking flow
organization_idObjectIdYesNoOrganization that owns the booking flow
contentBookingFlowContentYesNoContent configuration for the flow
content.titlestringYesNo*User-friendly title (1-100 characters)
stepsObjectId[]YesNoOrdered array of booking flow step references
statusBookingFlowPublishedStatusYesNoPublication status of the booking flow

*Title must be unique within an organization context

Status Types

enum BookingFlowPublishedStatus {
DRAFT = 'draft', // Flow is in draft mode, editable but not live
PUBLISHED = 'published', // Flow is published and available for bookings
DELETED = 'deleted' // Flow is marked as deleted and no longer available
}

API DTOs

Create DTO

type CreateBookingFlowDto = {
content: {
title: string; // Required, min length 1, max length 100
};
steps?: string[]; // Optional array of step IDs
};

Update DTO

type UpdateBookingFlowDto = {
content?: {
title: string; // Required if content is provided
};
steps?: string[]; // Optional array of step IDs
status?: BookingFlowPublishedStatus; // Optional status update
};

Filter DTO

type FilterBookingFlowDto = {
title?: string | { $regex: string }; // Optional title filter (exact or regex)
status?: BookingFlowPublishedStatus; // Optional status filter
populate?: boolean; // Whether to populate step references
};

Relationships

Primary Relationships

  • BookingFlow → Organization (Many-to-One, Required)

    • Each flow must belong to a specific organization
    • Referenced via organization_id field
    • Enforces multi-tenant isolation
    • Repository methods validate organization ownership
  • BookingFlow → BookingFlowStep (One-to-Many, Required)

    • Each flow contains an ordered sequence of steps
    • Referenced via steps array of ObjectIds
    • Steps must exist and belong to the same organization
    • Steps can be optionally populated in API responses

Validation

Step Ordering Validation

The ordering of steps in a booking flow follows strict business rules based on the step types.

Step Type Requirements and Order Logic

Step TypeRequired?Position Rules
ZipcodeNoIf included, must be the 1st step in the flow
Service SelectionYes*Must be 1st step if no Zipcode step exists, or 2nd step if Zipcode step exists
Additional DetailsNoIf included, must follow Service Selection step (can be 2nd or 3rd depending on Zipcode presence)
Timeslot SelectionYes*Must always be the 3rd to last step in the sequence
Contact InfoYes*Must always be the 2nd to last step in the sequence
SummaryYes*Must always be the last step in the sequence

*Required for PUBLISHED flows only. In DRAFT mode, no steps are required, but any included steps must follow the position rules.

Step Sequence Validation

  1. DRAFT Status Flow Validation:

    • Lenient validation to allow for work-in-progress flows
    • Can have an empty steps array
    • No steps are actually required in DRAFT mode
    • Any steps that are included must follow the correct sequence
    • Each step type can only occur once (no duplicates)
  2. PUBLISHED Status Flow Validation:

    • Strict validation to ensure complete and correct booking experiences
    • Cannot have an empty steps array
    • Must include ALL required steps (Service Selection, Timeslot Selection, Contact Info, Summary)
    • All steps must follow the exact ordering rules
    • Each step type can only occur once (no duplicates)
    • Must form a coherent, end-to-end booking experience

Validation Rules

  1. Duplicate Step Check

    • No duplicate step IDs are allowed in the steps array
    • Validation function converts step IDs to strings and checks uniqueness
    • Returns a specific error message: 'Duplicate step IDs found'
  2. Step Existence and Ownership

    • All referenced steps must exist in the database
    • Steps must belong to the same organization as the booking flow
    • MongoDB query verifies both existence and ownership
    • Query fetches step_type for each step to enable ordering validation
  3. Step Type Ordering

    • Steps are converted to {type, _id} format for validation
    • Shared utility function validateBookingFlowSteps enforces ordering rules
    • Different validation rules apply based on flow status
  4. Title Uniqueness

    • Flow titles must be unique within an organization
    • Repository has existsByTitleInOrganization method to check uniqueness
    • Update operations can exclude the current flow ID from uniqueness checks

Middleware Implementation

The booking flow schema implements mongoose middleware to ensure validation is consistent across all operations:

  1. Document Middleware

    • Pre-save hook validates document creation and updates via save()
    • Only triggers when steps or status fields are modified
    • Uses isModified() to efficiently skip validation when unnecessary
  2. Query Middleware

    • Pre-operation hooks for findOneAndUpdate, updateOne, updateMany
    • Detects updates to steps or status fields in both direct and $set operations
    • Fetches existing document to get the organization_id and unmodified fields
    • Uses the same validation function as document middleware for consistency

Implementation Examples

Creating a Booking Flow

// Example API request to create a booking flow
const createDto = {
content: {
title: "Standard Service Booking"
},
steps: [
"60f7b0b9e6b3f3b4e8b4b0b9", // Zipcode step
"60f7b0b9e6b3f3b4e8b4b0ba", // Service Selection step
"60f7b0b9e6b3f3b4e8b4b0bb" // Timeslot Selection step
]
};

// Internal flow in service layer
const newFlow = await bookingFlowRepository.create({
organization_id: new Types.ObjectId(organizationId),
content: createDto.content,
steps: createDto.steps?.map(id => new Types.ObjectId(id)) || [],
// Default status is DRAFT
status: BookingFlowPublishedStatus.DRAFT
});

Publishing a Flow

// Example API request to update a flow's status
const updateDto = {
status: BookingFlowPublishedStatus.PUBLISHED
};

// Internal flow in service layer
const updatedFlow = await bookingFlowRepository.findByIdAndUpdate(
flowId,
{ status: BookingFlowPublishedStatus.PUBLISHED },
// Validation middleware will run to ensure the flow can be published
{ new: true }
);

Updating Steps

// Example API request to update steps
const updateDto = {
steps: [
"60f7b0b9e6b3f3b4e8b4b0b9", // Zipcode step
"60f7b0b9e6b3f3b4e8b4b0ba", // Service Selection step
"60f7b0b9e6b3f3b4e8b4b0bd", // Additional Details step
"60f7b0b9e6b3f3b4e8b4b0bb" // Timeslot Selection step
]
};

// Internal flow in repository
const result = await this.bookingFlowModel.findOneAndUpdate(
{
_id: new Types.ObjectId(id),
organization_id: new Types.ObjectId(organizationId)
},
{ $set: { steps: updateDto.steps.map(id => new Types.ObjectId(id)) } },
{ new: true }
);

Security Considerations

  1. Multi-Tenant Isolation

    • Organization-scoped validation enforced in repository methods
    • Pre-hook validation confirms step ownership before save/update
    • Controller extracts organization ID from authenticated session
    • All API endpoints require appropriate permissions
  2. Status Transitions

    • Validation rules are stricter for PUBLISHED flows than DRAFT flows
    • Service layer enforces business rules for status transitions
    • BadRequestException thrown for invalid transitions
  3. Title Uniqueness

    • Repository checks title uniqueness within organization scope
    • Update operations exclude the current document from uniqueness checks
    • Clear error messages for title conflict resolution
  4. Input Validation

    • DTOs use class-validator for request validation
    • Mongoose schema enforces data structure integrity
    • Custom validators ensure business rules compliance

Performance Considerations

  1. Indexes

    • Compound index on { organization_id: 1, status: 1 }
    • Optimizes organization-scoped queries filtered by status
    • Used by findByOrganizationId and findPublishedByOrganizationId methods
  2. Step Population

    • Optional step population controlled via query parameter
    • Population uses discriminator support for polymorphic step types
    • Configurable through populateConfig in repository
  3. Validation Optimization

    • Early exit patterns for validation functions
    • Skips validation when no relevant fields are modified
    • Batches database queries for step validation
  4. Query Efficiency

    • Lean queries used for validation to reduce memory overhead
    • Repository implements normalized and non-normalized response options
    • Selective population of referenced entities