惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

M
MIT News - Artificial intelligence
罗磊的独立博客
Hugging Face - Blog
Hugging Face - Blog
Apple Machine Learning Research
Apple Machine Learning Research
Last Week in AI
Last Week in AI
S
SegmentFault 最新的问题
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
美团技术团队
人人都是产品经理
人人都是产品经理
WordPress大学
WordPress大学
The Cloudflare Blog
IT之家
IT之家
雷峰网
雷峰网
小众软件
小众软件
博客园 - 叶小钗
博客园 - 聂微东
爱范儿
爱范儿
博客园 - 司徒正美
博客园 - 三生石上(FineUI控件)
V
Visual Studio Blog
博客园 - 【当耐特】
V
V2EX
博客园_首页
T
Tailwind CSS Blog

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
Part 8: Persistence and State - EF Core, Migrations, and ...
Nick · 2026-06-17 · via DEV Community

In the last part, we looked at how expressions make your workflows dynamic. Today, we are discussing the backbone of any production-ready system: Persistence. How do we keep your workflow data, execution history, and node states safe, even if the server crashes or restarts?

EF Core: Our Reliable Partner

Vyshyvanka uses Entity Framework Core (EF Core) as the primary abstraction for database interaction. It allows us to work with our domain entities in a type-safe, object-oriented way while maintaining the flexibility to target different database backends.

For development, we default to SQLite because it makes setting up local testing environments trivial — no external services, no Docker containers, just a file. For production, we support PostgreSQL, which gives us the robustness, scalability, and feature set required for high-volume automation tasks.

The switch between backends is handled at startup via configuration and .NET Aspire service wiring, so the same application code works seamlessly against both.

The Migration Workflow

One of the most important rules in our development culture is this: Never use EnsureCreatedAsync() in production.

While it is tempting for quick prototyping, relying on auto-generation for production schemas is a recipe for disaster. We strictly use EF Core Migrations. This approach provides a version-controlled history of your database schema, ensuring that deployments are predictable and that we can roll back if something goes wrong.

Our command for adding a migration is standardized:

dotnet ef migrations add <Name> \
  --project src/Vyshyvanka.Engine \
  --startup-project src/Vyshyvanka.Api \
  --output-dir Persistence/Migrations

At startup, the application automatically applies any pending migrations with MigrateAsync(). This ensures that every deployment brings the schema up to date without manual intervention.

Separation of Concerns

Our persistence layer follows a clean separation:

Location Purpose
Vyshyvanka.Engine/Persistence/VyshyvankaDbContext.cs The EF Core DbContext
Vyshyvanka.Engine/Persistence/Entities/ Entity classes (DB table mappings)
Vyshyvanka.Engine/Persistence/Migrations/ Auto-generated migration files
Vyshyvanka.Core/Interfaces/ Repository interfaces
Vyshyvanka.Engine/Persistence/ Repository implementations

This ensures that the domain layer (Core) never depends on EF Core or any specific database technology. The engine provides the implementation details.

State Separation

We persist the workflow state (the graph structure — nodes, connections, configuration) and the execution state (the runtime status of every node during a run) separately. This separation allows us to perform high-frequency updates on the execution state without modifying the workflow definition itself.

When an execution transitions states, we use database transactions to ensure atomicity:

  1. The node execution result is saved.
  2. The overall execution status is updated.
  3. Terminal states (Completed, Failed, Cancelled) are final — no further writes allowed.

This prevents zombie executions or partially processed data from corrupting your results.

Async All the Way Down

Every repository method follows our async-first pattern with CancellationToken:

public async Task<Workflow?> GetByIdAsync(Guid id, CancellationToken ct)
{
    return await _context.Workflows
        .Include(w => w.Nodes)
        .Include(w => w.Connections)
        .FirstOrDefaultAsync(w => w.Id == id, ct);
}

This ensures that database I/O never blocks threads, keeping the engine responsive even under heavy load.

Testing Our Persistence

Because we use EF Core, we can leverage lightweight in-memory providers to write unit tests for our data access logic without needing a real database:

[Fact]
public async Task WhenWorkflowSavedThenCanBeRetrieved()
{
    var options = new DbContextOptionsBuilder<VyshyvankaDbContext>()
        .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
        .Options;

    await using var context = new VyshyvankaDbContext(options);
    var repository = new WorkflowRepository(context);

    var workflow = CreateTestWorkflow();
    await repository.SaveAsync(workflow, CancellationToken.None);
    var retrieved = await repository.GetByIdAsync(workflow.Id, CancellationToken.None);

    retrieved.Should().NotBeNull();
    retrieved!.Nodes.Should().HaveCount(workflow.Nodes.Count);
}

For integration tests, we use WebApplicationFactory with a real SQLite database to test the full API-to-database path.

Credential Storage

Credentials get special treatment in our persistence layer. The CredentialEntity stores encrypted data — never plain text. The encryption key is managed externally (environment variables or a secrets manager). Even if someone gets access to the database, the credential values remain protected by AES-256 encryption.

Persistence is the quiet backbone of Vyshyvanka. By respecting migrations, separating concerns, and testing at every level, we ensure that your workflow data is always safe, consistent, and recoverable.

In the next part, we will discuss Part 9: Security First - Credentials, Authentication, and Secrets Management. Stay tuned!


Check out the project source code here: https://github.com/homolibere/Vyshyvanka