DocView Capture by DocView.AI3 Nov 2026 09:49

Architecture and plugins

This page describes how DocView Capture is built, for developers and IT architects who integrate with it, extend it, or plan its hosting.

The programs

DocView Capture has three programs that share one SQL Server database and one storage location for page images and outputs.

Program Runs as What it does
Web app IIS, Azure App Service or a Linux container Every screen, the REST API, and live progress over SignalR.
DocView Worker Windows service, Azure WebJob or a Linux container; one or more OCR, separation, classification, extraction, export and import polling.
Scan Agent Tray app or Windows service on each scanning PC Connects TWAIN and WIA scanners to the browser.

Storage is a local folder or network share, or Azure Blob Storage. Page images and full OCR results are never stored in SQL Server. SQL holds metadata only, which keeps SQL Server Express within its 10 GB limit.

Clean architecture

The source code follows clean architecture. References point inwards only:

Web / Worker  →  Infrastructure  →  Application  →  Domain
Project Contains May reference
DocView.Capture.Domain Entities, value objects and domain rules. Nothing. No framework at all.
DocView.Capture.Application Use cases as plain application services, ports and the plugin contracts. Domain, and only the abstractions of EF Core.
DocView.Capture.Infrastructure EF Core and SQL Server, the SQL queue, storage, plugin implementations, scripting, encryption. Application, Domain.
DocView.Capture.Web MVC controllers and Razor views, REST API, SignalR hub, authentication. Infrastructure, Application, Domain.
DocView.Capture.Worker Hosted services that run automatic modules and import pollers. Infrastructure, Application, Domain.
DocView.Capture.ScanAgent The Windows Scan Agent. None of the above.

Architecture tests (tests/DocView.Capture.Architecture.Tests) enforce these rules. They also fail the build if a forbidden package is referenced.

Other design choices:

  • No mediator library. Each use-case area is a plain service, such as BatchClassService or IngestionService, injected through dependency injection.
  • Hand-written mapping. No mapping library.
  • Async throughout, structured logging, and EF Core migrations for every schema change.
  • Only permissive open-source licences (MIT, Apache 2.0, BSD, MS-PL). The notices they require ship with every installation in NOTICES.txt.

Batches, workflow and the queue

A batch holds documents, and documents hold pages. Every level carries field values and metadata.

Each Batch Setup version defines an ordered list of workflow steps. Scan is always first and PDF generation & export always last. Each step is its own queue. A module takes batches from its queue, processes them and routes them on: to the next step, to a later step, to a manual review step, or to the exception queue with a message.

The queue is the Batches table itself. DocView Workers claim work with a single atomic SQL statement using UPDLOCK, READPAST hints, so many DocView Workers can pull from the same queue without ever claiming the same batch twice. Automatic modules hold a lease they renew while working. If a DocView Worker stops, a sweeper returns its batches to the queue after the lease runs out. No message broker is needed.

Batches keep the Batch Setup version they started with. Releasing a new version never changes batches in progress.

Scaling

  • DocView Workers scale horizontally. Add servers or instances; they share the queue. Worker profiles decide what each DocView Worker does. See DocView Workers.
  • Web servers can run behind a load balancer with sticky sessions (for live progress).
  • The recognition module processes the pages of a batch in parallel.

Recognition pipeline

The automatic recognition step runs these stages in order:

image cleanup → barcode detection → OCR → separation → classification
    → extraction → validation → confidence check → route

The same pipeline powers the administrators' Test tab, without saving anything, so tests behave exactly like production.

Plugin interfaces

Engines, rules and connectors are plugins behind interfaces in DocView.Capture.Application/Plugins. Core logic depends only on the interfaces, never on a specific OCR engine or connector.

Interface Purpose Shipped implementations
IOcrEngine Text, lines and words with positions and confidence; optional key-value pairs and tables. Tesseract (tesseract), Azure AI Document Intelligence (azure), Google Cloud Vision (google).
IImageProcessor One image cleanup operation. Deskew, blank page detection, despeckle, border removal, punch-hole removal, auto-rotate.
IDocumentSeparator One separation rule. Votes per page. Fixed page count, page size and count, blank page, barcode, patch code, separator sheet, keyword, class change, new file, manual.
IClassifier Scores every candidate document class. Rules classifier, machine-learning text classifier.
IFieldExtractor One extraction method. Regex, key-value, zonal, tick boxes (OMR), anchor, barcode, table, cloud key-value.
IExportConnector Staged, transactional delivery. Folder, SFTP, SQL insert, REST, C# script.
IImportSource Pull-based import, polled by DocView Workers. Watched folder, email (IMAP/POP3).

Common rules for plugins

Every plugin implements IPlugin:

public interface IPlugin
{
    string Key { get; }         // stable, lower-case, stored in configuration
    string DisplayName { get; } // shown in the administration screens
}
  1. The Key is stored in Batch Setups, so it must never change once shipped.
  2. Plugins are registered in dependency injection as IEnumerable<TContract>. IPluginCatalog<TContract> finds them by key (case-insensitive) and fails at start-up on an empty or duplicate key.
  3. Settings are a JsonElement owned by the plugin and stored with the rule or step that uses it.
  4. Secrets are never inside settings. Settings hold the secret's name; the host resolves it through ISecretStore.
  5. Plugins must be usable from a Test action: pure functions of their inputs, with no hidden state.

Example: the OCR contract

public interface IOcrEngine : IPlugin
{
    OcrEngineCapabilities Capabilities { get; }
    Task<OcrPageResult> RecognizeAsync(OcrRequest request, CancellationToken cancellationToken);
}

public sealed record OcrRequest(
    IPageImage Page,
    IReadOnlyList<string> Languages,
    BoundingBox? Region = null,
    JsonElement? Settings = null);

Example: the export contract

public interface IExportConnector : IPlugin
{
    Task<ConnectorTestResult> TestAsync(JsonElement settings, CancellationToken cancellationToken);
    Task<IExportSession> BeginAsync(ExportContext context, CancellationToken cancellationToken);
}

public interface IExportSession : IAsyncDisposable
{
    Task WriteFileAsync(string relativePath, Stream content, string contentType, CancellationToken cancellationToken);
    Task WriteRecordAsync(string name, IReadOnlyDictionary<string, object?> values, CancellationToken cancellationToken);
    Task CommitAsync(CancellationToken cancellationToken);
    Task RollbackAsync(CancellationToken cancellationToken);
}

A session that is disposed without a commit must roll back, so no partial output is left behind. Each destination records its own delivery, so a retry skips destinations that already succeeded.

Adding a plugin

  1. Implement the interface in DocView.Capture.Infrastructure, with a new, unique key.
  2. Register it in Infrastructure/DependencyInjection.cs, for example services.AddScoped<IExportConnector, MyConnector>();.
  3. Describe its settings in Application/Plugins/SettingsSchemas.cs, so administrators get a form with labels, help texts and defaults.
  4. Add unit tests, and check the licence of any new package.

Note: Most administration screens list plugins from the catalog. The OCR engine choice on the Batch Setup's General tab lists the three shipped engines, so a new OCR engine also needs that list extended.

For customer-specific exports that do not justify a new plugin, use C# export scripts.

Scripting

Kind Language Where it runs
File and folder names, XML, CSV and JSON templates Scriban templates Sandboxed, with time and size limits.
Finders, rules, formatters, calculated fields, server event scripts JavaScript Sandboxed: no .NET, file or network access; time, statement and memory limits.
Browser event scripts JavaScript A Web Worker with no network access, stopped after 1.5 seconds.
C# export scripts C# On the DocView Worker with its rights. Not sandboxed; guarded by a permission and a switch.

See JavaScript event scripts.

Security design

  • ASP.NET Core Identity with cookie sign-in, and optional Windows (Negotiate) sign-in.
  • Permission-based policies per module, and per Batch Setup rights.
  • Secrets encrypted at rest with ASP.NET Core Data Protection; the key ring is stored in the database and protected by the machine or a certificate.
  • API keys stored as salted SHA-256 hashes.
  • A full audit trail, including every field value change.

Still stuck? support@docview.ai · +44 7886 864921

Made with DocView Capture by DocView.AI · www.docview.ai