Architecture
This page is for readers evaluating or extending the codebase rather than using the application.
Layering
Section titled “Layering”Four projects, with a strictly one-directional dependency chain:
┌──────────────────┐│ Docsentry.Web │ MVC controllers, Razor views, Identity pages,│ │ /api/v1 controllers, background job wiring└────────┬─────────┘ │ depends on┌────────v──────────────────┐│ Docsentry.Infrastructure │ Dapper repositories and query services,│ │ storage, previews, OCR, antivirus, email,│ │ workflows, the Identity EF Core context└────────┬──────────────────┘ │ depends on┌────────v─────────────────┐│ Docsentry.Application │ DTOs, service interfaces, validation and│ │ pagination contracts, exceptions, use cases└────────┬─────────────────┘ │ depends on┌────────v─────────────┐│ Docsentry.Domain │ Enums, value objects, permission masks,│ │ workflow state rules. No dependencies.└──────────────────────┘Nothing points back up the chain. Docsentry.Domain references no other project and no
infrastructure package - it holds pure rules, which is why the permission evaluator can be tested
without a database.
Split persistence
Section titled “Split persistence”The most important structural decision in the system, and the one most likely to be violated by accident:
- Entity Framework Core is used only for ASP.NET Core Identity. A single
DbContextowns theAspNet*tables - users, roles, claims, logins - and nothing else. - Every other table is reached through Dapper, with repositories for writes and query services for reads.
There is no DbContext for document management tables and no Entity Framework migrations for
them. Adding one would break the deployment model described below.
Why two strategies
Section titled “Why two strategies”Identity is a solved problem with a supported Entity Framework implementation; re-implementing it would be pure risk. The document management schema is the opposite case: it is the system’s centre of gravity, it carries stored procedures, computed permission tables, full-text indexes, and least-privilege grants, and it must be reviewable and deployable by a database administrator independently of an application release. Expressing that through a code-first migration chain would put the schema’s real complexity somewhere it cannot be reviewed.
Request flow
Section titled “Request flow”HTTP request │ vController ----------> never executes SQL, never opens a connection │ vApplication service -> use-case logic, validation, authorisation checks │ vRepository (writes) / Query service (reads) │ vDapper ────----------> parameterised SQL against SQL ServerControllers translate HTTP to a call and a result back to a response. That is all. A controller that opens a connection or writes SQL is a defect regardless of whether it works.
Conventions
Section titled “Conventions”These are enforced by tests in Docsentry.Application.Tests, so a violation fails the build
rather than surviving review.
| Convention | Rule |
|---|---|
| Cancellation | Every asynchronous controller, service, repository, storage, and SQL operation accepts and forwards a CancellationToken. |
| API shape | API controllers are [ApiController], live under versioned routes (/api/v1/...), and return DTOs via ActionResult<T>. |
| DTOs | Request and response bodies are sealed records. Database row models, Identity entities, and Dapper internals are never exposed. |
| Time | DateTimeOffset throughout. |
| SQL | Always parameterised. No string concatenation with user input. |
| MVC | Page controllers return views or redirects; API controllers return DTOs. The two are not mixed. |
The database is deployed, not migrated
Section titled “The database is deployed, not migrated”The document management schema ships as numbered, immutable, forward-only SQL scripts under
Database/deploy/, with a checksum ledger and a runner contract. Applied scripts are never
edited; corrections ship as new higher-numbered scripts. There is no rollback.
The application runs as a least-privilege principal that cannot alter schema, verified at startup. Full detail in Database deployment.
Background work
Section titled “Background work”Work that does not belong in a request - preview generation, text extraction, notification delivery, retention, integrity verification, permission rebuild - runs on scheduled jobs. There are no hand-rolled timer hosts; everything goes through the job runner, which persists its state in the database and provides an administrator-gated dashboard.
This is why an uploaded document may briefly show no thumbnail: the upload request stores the file and returns, and rendering happens shortly afterwards. See Background jobs.
Where things live
Section titled “Where things live”| Path | Contents |
|---|---|
src/Docsentry.Domain/ |
Enums, value objects, permission masks, workflow state rules. |
src/Docsentry.Application/Abstractions/ |
Service interfaces, grouped by domain. |
src/Docsentry.Application/Options/ |
Strongly typed options and their validators. |
src/Docsentry.Infrastructure/Data/ |
Connection factory, session and transaction runner, Dapper helpers, keyset paging. |
src/Docsentry.Infrastructure/Repositories/ |
Write-side Dapper implementations. |
src/Docsentry.Infrastructure/Queries/ |
Read-side query services. |
src/Docsentry.Infrastructure/Identity/ |
The one Entity Framework context, plus Identity services. |
src/Docsentry.Web/Controllers/ |
MVC controllers. |
src/Docsentry.Web/Controllers/Api/ |
Versioned JSON API controllers. |
src/Docsentry.Web/BackgroundJobs/ |
Job registration and runners. |
Database/deploy/ |
Numbered forward-only schema scripts. |
Database/verification/ |
Post-deployment contract and integrity checks. |
tools/Docsentry.Backup/ |
The encrypted backup command-line tool. |
Testing
Section titled “Testing”| Project | Responsibility |
|---|---|
Docsentry.Domain.Tests |
Pure domain rules. |
Docsentry.Application.Tests |
Contract and architecture assertions - the conventions above. |
Docsentry.Infrastructure.Tests |
Data access, paging, service behaviour, and the “no Entity Framework in DMS data access” invariant. |
Docsentry.Web.Tests |
In-memory host: routing, authorisation gates, localisation, rendering, design-system conventions. |
Docsentry.Backup.Tests |
Backup cryptography, manifests, and destinations. |
Docsentry.Database.IntegrationTests |
Requires a live SQL Server. |
Docsentry.Web.AcceptanceTests |
Browser-driven, opt-in, deliberately outside the solution so a normal build never pulls in browser downloads. |