elsa-core/doc/wiki/persistence.md
Sipke Schoorstra b9664a954d
[codex] Add codebase wiki (#7453)
* Add codebase wiki

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Document resilient restore workflow

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-16 11:54:27 +02:00

6.4 KiB

Persistence

Elsa uses replaceable stores. Most features default to memory stores, then provider packages replace those stores with EF Core or other persistence implementations. Diagnostics structured logs also have a separate relational/SQLite persistence path that deliberately does not use EF Core.

Store Replacement Pattern

Feature classes expose store factories. Persistence features replace those factories during Configure().

Example from EFCoreWorkflowRuntimePersistenceFeature:

  • replace WorkflowRuntimeFeature.TriggerStore
  • replace BookmarkStore
  • replace BookmarkQueueStore
  • replace WorkflowExecutionLogStore
  • replace ActivityExecutionLogStore
  • replace key-value store

Management persistence follows the same idea for definition and instance stores.

EF Core Shared Infrastructure

Shared EF Core infrastructure lives in Elsa.Persistence.EFCore.Common and Elsa.Persistence.EFCore.

Important base types:

PersistenceFeatureBase registers IDbContextFactory<TDbContext>, migration options, tenant-aware context factory decoration, and tenant model handlers.

EF Core Module Slices

The shared EF Core module contains slices for:

Each slice has a DbContext, configurations, store implementations, feature classes, and shell feature classes.

Provider Packages

Provider packages configure database-specific EF Core options and migrations:

Combined provider shell features such as SqliteWorkflowPersistenceShellFeature let modular hosts configure workflow persistence once and share settings with dependent definition, instance, and runtime persistence features.

Typical Host Configuration

The reference server configures SQLite persistence for management and runtime separately:

elsa.UseWorkflowManagement(management =>
{
    management.UseEntityFrameworkCore(ef => ef.UseSqlite());
});

elsa.UseWorkflowRuntime(runtime =>
{
    runtime.UseEntityFrameworkCore(ef => ef.UseSqlite());
});

See src/apps/Elsa.Server.Web/Program.cs.

Migrations

EF Core migrations are controlled by feature options such as RunMigrations. The base persistence feature registers startup tasks that run migrations when enabled.

Migration-related files:

When adding an entity or changing persisted shape, check every provider package and test provider-specific migration behavior where practical.

Tenant Awareness

EF Core persistence decorates IDbContextFactory<TDbContext> with TenantAwareDbContextFactory<TDbContext> and registers model/saving handlers:

  • ApplyTenantId
  • SetTenantIdFilter

Tenant conventions are documented in ADRs:

Structured Log Persistence

Structured log persistence is intentionally separate from EF Core. The active feature plan is 005 structured log persistence.

Packages:

This path uses explicit SQL and FluentMigrator. It stores timestamps as UTC ISO-8601 text and JSON payloads as text in SQLite.

Adding A Store

When adding a new store implementation:

  1. Identify the feature contract that owns the store.
  2. Keep the core module provider-neutral.
  3. Add the concrete store in the persistence/provider module.
  4. Replace the feature's store factory in the persistence feature.
  5. Add unit tests for store-specific query behavior if logic is nontrivial.
  6. Add integration tests for provider behavior, migrations, and multi-target concerns where practical.

Persistence Risk Checklist

  • Does the change affect multiple target frameworks?
  • Does it need provider-specific migrations?
  • Does it preserve tenant filtering?
  • Does it update both definition and instance stores if both shapes changed?
  • Does it require API/client DTO updates?
  • Does it alter runtime recovery, bookmark, or trigger semantics?
  • Does it need retention, cleanup, or migration documentation?