Architecture
High-level overview of Elsa Workflows v3 architecture, covering workflow execution flow, core concepts like bookmarks and triggers, workflow runtimes, and multitenancy.
This guide provides a high-level understanding of Elsa's architecture and what happens when a workflow executes. Whether you're extending Elsa, troubleshooting issues, or simply curious about how everything fits together, this overview will help you understand the system's core concepts and structure.
What Happens When a Workflow Executes?
At a high level, when a workflow executes in Elsa:
Dispatch: A workflow is dispatched for execution via
IWorkflowDispatcher, which enqueues the requestLoad: The workflow definition is loaded from storage (or cache)
Instantiate: A workflow instance is created or resumed (if continuing from a bookmark)
Execute: Activities run sequentially or in parallel based on the workflow structure
Bookmark (Optional): If an activity creates a bookmark, execution pauses and state is persisted
Complete or Suspend: The workflow either completes or suspends, waiting for external stimuli to resume
This lifecycle repeats as workflows are triggered, execute activities, wait for events, and continue execution.
Core Concepts
Execute vs Dispatch
Understanding the difference between executing and dispatching workflows is fundamental:
Execute (IWorkflowRunner)
Direct, synchronous execution of a workflow in the current process
No queuing or background processing
Useful for testing, simple workflows, or when immediate results are needed
Does not involve
IWorkflowDispatcher
// Direct execution
var result = await workflowRunner.RunAsync(
new RunWorkflowRequest
{
DefinitionId = workflowId
});Dispatch (IWorkflowDispatcher)
Asynchronous, queue-based workflow execution
Requests are enqueued and processed by background workers
Supports distributed execution across multiple nodes
Default approach for production workflows
// Dispatching for background execution
await workflowDispatcher.DispatchAsync(
new DispatchWorkflowDefinitionRequest
{
DefinitionId = workflowId
});IWorkflowRuntime
High-level abstraction that combines runtime management with persistence
Provides client API for workflow operations (start, resume, cancel, etc.)
Used by most applications for workflow lifecycle management
Internally uses
IWorkflowDispatcherfor execution
For a deep dive into dispatching, see the Workflow Dispatcher Architecture guide.
Bookmarks, Triggers, and Stimuli
These three concepts work together to enable event-driven, long-running workflows:
Bookmarks
A bookmark is a "pause point" in a workflow. When an activity creates a bookmark:
The workflow's current state is persisted
Execution suspends at that activity
The bookmark waits for a matching stimulus to resume execution
Think of bookmarks as save points in a video game - the workflow can be resumed from exactly where it left off.
Common activities that create bookmarks:
Event- Waits for a named eventDelay- Waits for a timerHTTP Endpoint- Waits for an HTTP requestSend HTTP Request(when configured to wait for response)
Triggers
A trigger is a special type of activity that starts a workflow automatically when certain conditions are met. Triggers:
Create bookmarks when the workflow is published
Listen for external events (HTTP requests, timers, messages, etc.)
Automatically start new workflow instances when triggered
Are typically the first activity in a workflow
Example triggers:
HTTP Endpoint- Starts workflow when an HTTP request arrivesTimer- Starts workflow on a schedule (cron, interval, etc.)Message Received- Starts workflow when a message arrives
Stimuli
A stimulus is an external event that resumes a suspended workflow. Stimuli:
Are dispatched into the system via
IWorkflowDispatcherMatch against existing bookmarks
Resume workflows from their bookmarked position
The relationship:
Activity creates a bookmark (workflow pauses)
External event occurs and generates a stimulus
Stimulus matches bookmark and resumes execution
Workflow -> [Activity] -> Bookmark Created -> Persisted
↓
External Event -> Stimulus Dispatched -> Bookmark Matched -> Workflow ResumesWorkflow Runtimes
The workflow runtime is the execution environment that manages workflow lifecycle and state. Key responsibilities:
State Management
Tracks workflow instances and their current state
Persists bookmarks and activity data
Manages workflow variables and outputs
Execution Coordination
Schedules activities for execution
Handles activity outcomes and connections
Manages parallel execution paths
Event Processing
Receives and routes stimuli to bookmarks
Handles workflow triggers
Coordinates distributed execution
Integration Points
The runtime integrates with several subsystems:
Persistence: Storage for workflow definitions and instances
Dispatcher: Queuing and background execution
Activity Registry: Discovery of available activities
Expression Evaluators: Dynamic value resolution
Multitenancy (Conceptual Overview)
Elsa supports multitenancy - running multiple isolated tenants in a single deployment:
Tenant Isolation
Each tenant has isolated workflow definitions, instances, and data
Tenants cannot access each other's workflows or data
Tenant context is established early in the request pipeline
Tenant Resolution
Tenants are identified via tenant ID (from headers, routes, claims, etc.)
Tenant resolver strategies can be customized
Default tenant is available for single-tenant scenarios
Use Cases
SaaS Applications: Each customer is a tenant with isolated workflows
Multi-Organization: Departments or divisions with separate workflows
Development/Staging/Production: Logical separation within a deployment
For detailed multitenancy setup, see the Multitenancy Introduction guide.
System Architecture (Mindmap)
Here's a textual representation of Elsa's core functionality and surrounding modules:
Elsa Workflows v3
│
├── Core Engine
│ ├── IWorkflowRunner (Direct execution)
│ ├── IWorkflowRuntime (Runtime management + persistence)
│ ├── IWorkflowDispatcher (Queue-based execution)
│ ├── Activity Registry (Available activities)
│ ├── Workflow Definition Store (Workflow definitions)
│ └── Workflow Instance Store (Running/suspended workflows)
│
├── Execution Model
│ ├── Bookmarks (Pause points)
│ ├── Triggers (Auto-start workflows)
│ ├── Stimuli (Resume signals)
│ └── Activity Execution Context
│
├── Extensibility
│ ├── Modules & Features (Plugin architecture)
│ ├── Custom Activities (Domain-specific operations)
│ ├── Expression Evaluators (C#, JavaScript, Liquid, Python)
│ └── Middleware Pipeline (Request/response interception)
│
├── Persistence Layer
│ ├── Entity Framework Core (SQL Server, PostgreSQL, SQLite, MySQL)
│ ├── MongoDB (Document store)
│ ├── Dapper (Lightweight SQL)
│ └── In-Memory (Testing/development)
│
├── Integration Packages
│ ├── HTTP (REST APIs, webhooks)
│ ├── Email (SMTP, SendGrid, etc.)
│ ├── MassTransit (Message bus integration)
│ ├── Timers (Scheduled execution)
│ └── JavaScript/C# (Scripting)
│
├── Elsa Server (Backend API)
│ ├── REST API (Workflow management, execution control)
│ ├── WebSocket (Real-time updates)
│ ├── Authentication & Authorization
│ └── Multitenancy Support
│
├── Elsa Studio (Frontend UI)
│ ├── Workflow Designer (Visual editor)
│ ├── Activity Property Editors
│ ├── Instance Monitoring
│ └── Configuration UI
│
└── Deployment & Scaling
├── Distributed Hosting (Multiple nodes)
├── Clustering (Shared state)
├── Background Workers (Queue processing)
└── Load Balancing (Request distribution)Key Packages and Their Roles
Elsa.Workflows.Core
Core workflow engine, activities, and abstractions
Elsa.Workflows.Runtime
Runtime services for execution, persistence, and management
Elsa
Meta-package that includes commonly needed packages
Elsa.EntityFrameworkCore
EF Core persistence providers
Elsa.MongoDb
MongoDB persistence provider
Elsa.Dapper
Dapper-based persistence (lightweight SQL)
Elsa.Http
HTTP activities and triggers
Elsa.Email
Email sending activities
Elsa.MassTransit
Message bus integration
Elsa.JavaScript
JavaScript expression evaluator
Elsa.CSharp
C# expression evaluator
Elsa.Liquid
Liquid template expression evaluator
Elsa.Alterations
Workflow alteration and migration support
Typical Workflow Execution Flow
Here's a detailed look at what happens during a typical workflow execution:
1. Trigger Event
HTTP Request -> Elsa Server -> Trigger Matched -> Dispatch Workflow2. Workflow Dispatch
IWorkflowDispatcher.DispatchAsync()
↓
Enqueue Request -> Background Worker Picks Up
↓
Load Workflow Definition -> Create/Resume Instance3. Activity Execution
For each activity in sequence:
↓
Load Activity Definition
↓
Evaluate Input Expressions (variables, outputs, etc.)
↓
Execute Activity Logic
↓
Process Outcomes (determine next activities)
↓
[If bookmark created] -> Persist State & Suspend
[Otherwise] -> Continue to next activity4. Completion or Suspension
[All activities complete]
↓
Workflow Status = Completed
↓
Persist Final State -> Trigger Completion Events
[Bookmark created]
↓
Workflow Status = Suspended
↓
Persist Bookmark & State -> Wait for Stimulus5. Resume from Bookmark (if suspended)
External Event -> Stimulus Dispatched
↓
Match Bookmark -> Load Workflow Instance
↓
Resume from Bookmarked Activity
↓
Continue Execution (goto step 3)Understanding the Module System
Elsa's architecture is built around a module and feature system that enables clean extensibility:
Modules
A module is a container for related features. The IModule interface represents a configuration point where features can be registered and configured.
Features
A feature is a self-contained unit of functionality that:
Registers services with dependency injection
Adds activities to the activity registry
Configures workflow options
Can depend on other features
Registration Pattern
Features are registered using a fluent API:
builder.Services.AddElsa(elsa => elsa
.UseWorkflowRuntime()
.UseHttp()
.UseEmail()
.UseJavaScript()
);Each UseXyz() method adds a feature to the module, which then configures the necessary services.
For detailed information on creating custom modules and plugins, see the Modules and Plugins guide.
Further Reading
To dive deeper into specific aspects of Elsa's architecture:
Workflow Dispatcher Architecture - Deep dive into
IWorkflowDispatcherand dispatching patternsModules and Plugins - Learn how to extend Elsa with custom modules and activities
Custom Activities - Create domain-specific activities
Multitenancy Setup - Configure multitenancy in your application
Clustering - Scale Elsa across multiple nodes
Persistence Strategies - Choose and configure a persistence provider
Summary
Elsa's architecture is designed for flexibility and scalability:
Multiple execution models (direct, runtime, dispatched) for different scenarios
Event-driven patterns (bookmarks, triggers, stimuli) for long-running workflows
Modular design enabling clean extensibility
Multi-persistence support for different storage needs
Multitenancy for SaaS and multi-organization deployments
Understanding these core concepts will help you effectively build, extend, and troubleshoot Elsa-based workflow solutions.
Last updated