# 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](../../src/modules/Elsa.Persistence.EFCore/Modules/Runtime/WorkflowRuntimePersistenceFeature.cs): - 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](../../src/modules/Elsa.Persistence.EFCore.Common) and [Elsa.Persistence.EFCore](../../src/modules/Elsa.Persistence.EFCore). Important base types: - [PersistenceFeatureBase](../../src/modules/Elsa.Persistence.EFCore.Common/PersistenceFeatureBase.cs) - [PersistenceShellFeatureBase](../../src/modules/Elsa.Persistence.EFCore.Common/PersistenceShellFeatureBase.cs) - [CombinedPersistenceShellFeatureBase](../../src/modules/Elsa.Persistence.EFCore.Common/CombinedPersistenceShellFeatureBase.cs) - [ElsaDbContextBase](../../src/modules/Elsa.Persistence.EFCore.Common/ElsaDbContextBase.cs) `PersistenceFeatureBase` registers `IDbContextFactory`, migration options, tenant-aware context factory decoration, and tenant model handlers. ## EF Core Module Slices The shared EF Core module contains slices for: - [Management](../../src/modules/Elsa.Persistence.EFCore/Modules/Management) - [Runtime](../../src/modules/Elsa.Persistence.EFCore/Modules/Runtime) - [Identity](../../src/modules/Elsa.Persistence.EFCore/Modules/Identity) - [Tenants](../../src/modules/Elsa.Persistence.EFCore/Modules/Tenants) - [Labels](../../src/modules/Elsa.Persistence.EFCore/Modules/Labels) - [Alterations](../../src/modules/Elsa.Persistence.EFCore/Modules/Alterations) 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: - [Elsa.Persistence.EFCore.Sqlite](../../src/modules/Elsa.Persistence.EFCore.Sqlite) - [Elsa.Persistence.EFCore.SqlServer](../../src/modules/Elsa.Persistence.EFCore.SqlServer) - [Elsa.Persistence.EFCore.PostgreSql](../../src/modules/Elsa.Persistence.EFCore.PostgreSql) - [Elsa.Persistence.EFCore.MySql](../../src/modules/Elsa.Persistence.EFCore.MySql) - [Elsa.Persistence.EFCore.Oracle](../../src/modules/Elsa.Persistence.EFCore.Oracle) 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: ```csharp elsa.UseWorkflowManagement(management => { management.UseEntityFrameworkCore(ef => ef.UseSqlite()); }); elsa.UseWorkflowRuntime(runtime => { runtime.UseEntityFrameworkCore(ef => ef.UseSqlite()); }); ``` See [src/apps/Elsa.Server.Web/Program.cs](../../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: - [MigrationOptions](../../src/modules/Elsa.Persistence.EFCore.Common/MigrationOptions.cs) - [RunMigrationsStartupTask](../../src/modules/Elsa.Persistence.EFCore.Common/RunMigrationsStartupTask.cs) - [scripts/migrations/README.md](../../scripts/migrations/README.md) 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` with `TenantAwareDbContextFactory` and registers model/saving handlers: - `ApplyTenantId` - `SetTenantIdFilter` Tenant conventions are documented in ADRs: - [ADR 0008: Empty String As Default Tenant ID](../adr/0008-empty-string-as-default-tenant-id.md) - [ADR 0009: Asterisk Sentinel Value For Tenant-Agnostic Entities](../adr/0009-asterisk-sentinel-value-for-tenant-agnostic-entities.md) ## Structured Log Persistence Structured log persistence is intentionally separate from EF Core. The active feature plan is [005 structured log persistence](../../specs/005-structured-log-persistence/plan.md). Packages: - [Elsa.Diagnostics.StructuredLogs](../../src/modules/Elsa.Diagnostics.StructuredLogs): core capture, API, hub, provider/store contracts, default in-memory store. - [Elsa.Diagnostics.StructuredLogs.Persistence.Relational](../../src/modules/Elsa.Diagnostics.StructuredLogs.Persistence.Relational): provider-neutral relational store, SQL builder, mapper, retention service, write buffer. - [Elsa.Diagnostics.StructuredLogs.Persistence.Sqlite](../../src/modules/Elsa.Diagnostics.StructuredLogs.Persistence.Sqlite): SQLite connection factory, dialect, FluentMigrator runner, startup migration/cleanup service. 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?