On this page

Customer operations: a connected showcase

A business application rarely ends at a form. It stores customer details, gathers information from several services, and prepares reports that take longer than an HTTP request should wait. This walkthrough connects those jobs in one application design.

Start small: validate and register an inactive customer, then look that customer up. Add a dashboard with independent concurrent branches. Finally, submit a report to a bounded queue, inspect its status and cancel it through an API. The report processes invoice snapshots with bounded parallel work; an optional feature makes the export pipeline readable.

Follow the application

Step What you can explain afterward
1. Model and validation Ordinary classes, FluentValidation selectors and an optional Validator class kind
2. Application operations One registration policy shared by HTTP and non-HTTP callers
3. Persistence Dapper parameters, missing rows, connection ownership and cancellation
4. HTTP host Requests, responses, configuration and ordinary Microsoft DI
5. Checks and changes Expected outcomes and how a small policy change crosses layers
6. Concurrent dashboard Inline run branches, shared capacity and publication after joins
7. Queued reports Four active workers, bounded admission, job states and cancellation endpoints
8. Parallel processing and a feature Independent snapshots, CPU scheduling, ordered results and optional pipeline vocabulary
Reference C# and cases Native contracts, cancellation races, tested behavior and verification limits

The application flow is:

HTTP customer request → validation → application service → repository → SQL
                                  ↘ property errors / absence / infrastructure failure

HTTP report request → access check → bounded queue → four worker bodies
                                            ↓             ↓
HTTP status/cancel ← owner-scoped job state ← renderer → invoice snapshots
                                                           ↓
                                               bounded projection → report

An accepted report job survives the end of its submitting request. Cancellation is a separate operation on that job. Queue capacity, active worker count, database capacity and CPU capacity protect different things; increasing one does not automatically increase the others.

Files and dependencies

The registration portion uses one set of declarations. Add each declaration once; the class-kind version replaces the ordinary validator header rather than creating a duplicate class.

File / group Language and responsibility
Customer.ds, CustomerValidator.ds, optional Validator.ds DirectSharp model and validation; FluentValidation
CustomerOnboarding.ds Optional console caller for the first step
CustomerRegistrationService.ds Registration and lookup policy
CustomerKey.ds, CustomerStorageException.ds, CustomerRepository.ds Parameter model, error boundary and Dapper operations
Customers.sql SQL Server schema managed through normal migrations
CustomersController.ds DirectSharp HTTP actions using ASP.NET Core attributes, DI and request cancellation
CustomerDashboard.ds, CustomerDashboardClient.ds Dashboard projection and owned request branches
ReportWorkers.ds Bounded consumers and a scoped renderer per job
CustomerReportRenderer.ds, optional StatementRowsFeature.ds Invoice projection and optional export adapter
Report contracts, ReportJob, ReportQueue, ReportJobsController, ReportHostedService Ordinary application support contracts; DirectSharp workers and HTTP examples call them directly, with native reference sources linked separately
IInvoiceSource and ICustomerAccess implementations Application-specific database snapshot and access-policy integrations

The registration host references FluentValidation, Dapper and Microsoft.Data.SqlClient using ordinary .NET project configuration. Reporting also uses the shared ASP.NET Core framework, System.Threading.Channels and ordinary threading/task APIs. It does not require a broker or a new language service container. The reference checks pin FluentValidation 11.11.0 and Dapper 2.1.66; deployment dependencies must follow the actual implementation's supported targets.

The report contracts and application support examples use namespace DirectSharp.Showcase. That is a sample application namespace, not a proposed standard-library package. The DirectSharp renderer/worker alternatives replace their native reference equivalents; do not install both same-purpose implementations into the host.

ICustomerAccess must apply the application's real customer-access policy. IInvoiceSource must read independent invoice snapshots using its supplied token and release its reader/connection before returning. These integrations depend on the application's identity system and invoice schema; their exact contracts and controlled reference implementations are on the reference page. The customer registration/lookup SQL is fully shown. No invoice schema, external dashboard service or authentication provider is silently supplied.

Begin with model and validation.