ClinicFlow uses PostgreSQL 17 as its primary data store, accessed through Entity Framework Core with the Npgsql provider. All schema changes are version-controlled as EF Core migrations stored inDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/0Crazy-0/ClinicFlow/llms.txt
Use this file to discover all available pages before exploring further.
ClinicFlow.Infrastructure/Migrations/. This page explains how the context is configured, how soft-delete filtering works, how to manage migrations from the CLI, and how the seeding layer generates realistic development data.
ApplicationDbContext
ApplicationDbContext is the single EF Core DbContext for the entire application. It is defined in ClinicFlow.Infrastructure.Persistence and exposes a DbSet<T> property for each of the ten aggregate roots and supporting entities:
OnModelCreating performs two cross-cutting configuration passes before delegating to the per-entity Fluent API configurations:
- Domain event exclusion —
BaseEntity.DomainEventsis a transient in-memory collection that must not be persisted. The method iterates over allBaseEntitysubtypes and callsIgnore(nameof(BaseEntity.DomainEvents)). - Soft-delete global query filters — described in detail below.
- Assembly scan —
modelBuilder.ApplyConfigurationsFromAssembly(...)picks up allIEntityTypeConfiguration<T>classes in the Infrastructure assembly.
Soft-Delete Global Query Filters
Several entities inherit fromSoftDeletableEntity, which adds an IsDeleted boolean column. Rather than scattering .Where(e => !e.IsDeleted) throughout every query, ApplicationDbContext registers a global query filter at model creation time so that soft-deleted rows are automatically excluded from every LINQ query against those entity sets.
Because EF Core’s HasQueryFilter requires a strongly-typed lambda and the entity type is only known at runtime, the filter expression is built dynamically via the System.Linq.Expressions API:
context.Patients.ToListAsync() silently adds WHERE "IsDeleted" = FALSE. To query soft-deleted rows explicitly, use IgnoreQueryFilters():
Column Name Constants
EF Core Fluent API configurations reference column names through theColumnNames internal static class in ClinicFlow.Infrastructure.Persistence. This prevents magic strings from spreading across configuration files:
Connection String Configuration
The connection string is loaded from theDatabase section of appsettings.json using the DatabaseOptions class:
appsettings.json (or environment-specific overrides), add:
ApplicationDbContextFactory (Design-Time)
EF Core CLI tools (dotnet ef migrations add, dotnet ef database update) need to instantiate ApplicationDbContext without a running host application. ApplicationDbContextFactory fulfills this role by implementing IDesignTimeDbContextFactory<ApplicationDbContext>:
Managing Migrations
Apply all pending migrations
Create a new migration
After modifying entity configurations or adding new entities, generate a migration snapshot:<MigrationName> with a descriptive PascalCase name, for example AddPatientEmergencyContact. The generated files appear in ClinicFlow.Infrastructure/Migrations/.
Revert the last migration (before applying to the database)
Roll back to a specific migration
Seeding Layer
TheClinicFlow.Infrastructure/Persistence/Seeding/ directory contains a self-contained, deterministic data generator for local development. The entry point is DbSeeder, which orchestrates the entire seeding pipeline:
| Step | What is created |
|---|---|
SeedMedicalSpecialtiesAsync | 15 medical specialties (General Medicine, Cardiology, Oncology, etc.) |
SeedClinicalFormTemplatesAsync | Clinical form template definitions |
SeedAppointmentTypeDefinitionsAsync | Appointment type catalogue with age policies and required templates |
SeedUsersAsync | 3 admins, 8 receptionists, 35 doctors, 120 patients |
SeedDoctorsAsync | 35 doctor profiles with consultation rooms and specialties |
SeedPatientsAsync | 105 self patients + 15 older patients + 80 family-member dependents |
SeedSchedulesAsync | Weekly schedules for all 35 doctors |
SeedAppointmentsAsync | 500 appointments across all statuses via AppointmentGenerator |
SeedMedicalRecordsAsync | 250 medical records linked to completed appointments |
SeedPatientPenaltiesAsync | Automatic penalties for no-shows and late cancellations |
AppointmentGenerator produces deterministic appointments by cycling through a fixed distribution of statuses (250 Completed, 100 Scheduled, 50 Cancelled, and so on) and matching each appointment to eligible doctors, patients, and schedules.
The seeder uses Randomizer.Seed = new Random(42) so every fresh run produces exactly the same dataset.