Technical README - Project status, architecture and how to run
AsyncPdfProcessor is a .NET9 application that generates PDF reports of Turkish Central Bank exchange rates asynchronously. The system queues requests, fetches exchange rates from the TCMB XML feed, renders a tabular PDF report and stores it on the local file system using the implemented storage strategy.
Implemented features (current repository state):
- HTTP API endpoints to queue and download reports
POST /api/reports: queues a report generation for specificExchangeRateDateGET /api/reports/{referenceNo}/status: checks job statusGET /api/reports/{referenceNo}/download: downloads completed PDF (application/pdf)- Background job processing using Hangfire with SQL Server storage
- TCMB client that parses the official XML feed and normalizes numeric values
- PDF generation using QuestPDF producing a tabular report with columns:
Unit,Code,Name,Buying,Selling - EF Core
AppDbContextwith existing migrations undersrc/AsyncPdfProcessor.Infrastructure/Migrations - Implemented
IReportStorageStrategyand concrete local storage:LocalFileStorageStrategy
src/AsyncPdfProcessor.Api/-> Web API project exposing HTTP endpoints and wiring infrastructureProgram.cs- app startup and DI wiringEndpoints/ReportEndpoints.cs- API route definitionsModels/Request/ReportRequest.csandModels/Response/*- DTOsAsyncPdfProcessor.Infrastructure/-> Implementation of clients, services and persistenceClients/CentralBankClient.cs- TCMB XML clientServices/PdfReportGenerator.cs- Hangfire job handler and PDF generation (QuestPDF)Storages/LocalFileStorageStrategy.cs- local disk storage implementation ofIReportStorageStrategyPersistence/AppDbContext.cs- EF Core DbContext and migrationsAsyncPdfProcessor.Application/-> Application layer interfaces and service contractsAsyncPdfProcessor.Domain/-> Domain models (ExchangeRate, ReportJob entity, enums)tests/AsyncPdfProcessor.Tests/-> Integration / unit tests
This project uses Hangfire to run report generation jobs in the background. The relevant implementation details:
-
Storage
-
Hangfire is configured to use SQL Server storage in
Program.csviaUseSqlServerStorage(...)with a custom schema nameHangFire. -
Connection string is read from configuration key
ConnectionStrings:DefaultConnection. -
Enqueue flow
-
The API layer calls
IReportService.QueueReportGenerationAsync(exchangeRateDate)when a report request is received. -
ReportService.QueueReportGenerationAsynccreates aReportJobentity, persists it to the database (ReportJobstable) and then enqueues a Hangfire background job usingIBackgroundJobClient.Enqueue<T>. -
The enqueued method signature is
IPdfReportGenerator.ExecuteAsync(Guid reportJobId); Hangfire resolvesIPdfReportGeneratorfrom DI when executing the job. -
Job execution lifecycle
-
PdfReportGenerator.ExecuteAsyncis the background worker entry point. Typical steps inside the job:
- Load the
ReportJobrecord by id from the DB. - Update job status to
Processingand save. - Fetch exchange rates from the TCMB client.
- Generate PDF bytes (QuestPDF) and persist using the configured
IReportStorageStrategy(currentlyLocalFileStorageStrategy). - Update
ReportJob.StoragePath, set status toCompleted, setCompletedAtand save. - On exception, set
ReportJob.Status = Failed, storeFailureReasonand rethrow to let Hangfire record the failure.
-
Retry behavior
-
PdfReportGeneratorhas anAutomaticRetryattribute (configured with Attempts =3) — this instructs Hangfire to retry failed executions up to the configured number of attempts. -
Transient errors from the TCMB client (HTTP failures) are surfaced as exceptions so Hangfire retries the job according to the retry policy.
-
Worker hosting
-
The project registers Hangfire server via
builder.Services.AddHangfireServer()allowing the same web application to process jobs inline (single-process worker). For production, you can run a separate dedicated worker process by hostingAddHangfireServerin a separate worker app or service (not required by current repository state). -
Observability
-
Hangfire stores job state and history in the configured SQL database. You can inspect job state using Hangfire dashboard if added; the dashboard is not currently hooked up in the default code but can be enabled easily by registering
app.UseHangfireDashboard()inProgram.csand protecting it as needed.
- API: Minimal API using .NET9
WebApplicationand OpenAPI; routes under/api/reports. - Background Worker: Hangfire for queued, retriable background jobs stored in SQL Server.
- Data Access: EF Core
AppDbContextwith migrations present in the repository. - External integration: TCMB public XML feed (
CentralBank:ApiUrlin configuration). - PDF Generation: QuestPDF producing A4 tabular reports.
- Storage:
LocalFileStorageStrategypersists generated PDFs to disk.
The project contains a concrete implementation LocalFileStorageStrategy at src/AsyncPdfProcessor.Infrastructure/Storages/LocalFileStorageStrategy.cs which implements IReportStorageStrategy.
Behavior:
- Storage directory:
${ContentRootPath}/LocalReports(the API application's content root path +LocalReports). - File naming:
{reportId}.pdf(GUID-based file name). - API download:
GET /api/reports/{referenceNo}/downloadstreams the file found at the stored path with content typeapplication/pdf.
EF Core migrations are included in the repository under src/AsyncPdfProcessor.Infrastructure/Migrations. Apply migrations using dotnet ef with the infrastructure project as the --project and the API as the --startup-project.
Example commands (run from repository root):
# install tooling if needed
dotnet tool install --global dotnet-ef
# apply migrations
dotnet ef database update \
--project src/AsyncPdfProcessor.Infrastructure \
--startup-project src/AsyncPdfProcessor.ApiNotes:
--projecttargets the project containing theDbContext(Infrastructure)--startup-projecttargets the runnable project used for configuration and resolving connection strings (Api)
Prerequisites:
- .NET9 SDK
- SQL Server instance and a connection string configured as
ConnectionStrings:DefaultConnectioninsrc/AsyncPdfProcessor.Api/appsettings.*.json
Steps:
dotnet restoredotnet build- Apply migrations (see Database migrations section)
dotnet run --project src/AsyncPdfProcessor.Api
The API will be available on the configured applicationUrl (see src/AsyncPdfProcessor.Api/Properties/launchSettings.json).
CentralBank:ApiUrl- URL to TCMB XML feedConnectionStrings:DefaultConnection- SQL Server connection string used by Hangfire and EF Core
POST /api/reportswith{ "ExchangeRateDate": "2025-11-06" }returns areferenceNo(Guid).- Background job generates PDF and saves it using
LocalFileStorageStrategy. GET /api/reports/{referenceNo}/downloadreturns the PDF stream withapplication/pdf.