Clean Architecture with 4 layers following Microsoft best practices:
├── Domain (Entities, Interfaces, Config models) — No dependencies
├── Application (Business logic, Orchestration) — Depends on Domain
├── Infrastructure (Dapper/PostgreSQL, SFTP, ZIP) — Depends on Domain
└── Worker (Quartz Job, DI, Program.cs) — Depends on all
psql -d embase -f Database/create_tracking_table.sql
This creates tblEmbaseConferenceDispatch tracking table.
Configure per environment in src/EmbaseConferenceScheduler.Worker/:
| File | Purpose |
|---|---|
appsettings.json |
Common settings (Serilog, defaults) |
appsettings.Development.json |
Local dev overrides |
appsettings.Staging.json |
Staging environment |
appsettings.Production.json |
Production environment |
Key settings:
ConnectionStrings:EmbaseDb - PostgreSQL connectionSftp - SFTP server detailsPackaging - PDF paths, ZIP namingScheduler:CronExpression - Job schedulecd src/EmbaseConferenceScheduler.Worker
dotnet run --environment Development
dotnet run --environment Staging
dotnet run --environment Production
dotnet build -c Release
Configure appsettings files:
All settings are in src/EmbaseConferenceScheduler.Worker/appsettings.{Environment}.json
appsettings.Production.json for productionappsettings.Staging.json for stagingappsettings.Development.json for local developmentBuild Docker image:
docker build -t embase-conference-scheduler:latest .
Run container:
docker run -d \
-e DOTNET_ENVIRONMENT=Production \
-v /data/production/articles/pdf:/production/articles/pdf:ro \
-v embase-logs:/logs \
--name embase-conference-scheduler \
embase-conference-scheduler:latest
View logs:
docker logs -f embase-conference-scheduler
Stop:
docker stop embase-conference-scheduler
docker rm embase-conference-scheduler
Override any appsettings value via environment variables:
# Database
ConnectionStrings__EmbaseDb="Host=...;Database=...;Username=...;Password=..."
# SFTP
Sftp__Host="sftp.example.com"
Sftp__Username="user"
Sftp__Password="password"
# Scheduler
Scheduler__CronExpression="0 0 3 * * ?"
# Packaging
Packaging__PdfSourcePath="/custom/path"
Default schedules:
| Environment | CRON | Time |
|---|---|---|
| Development | 0 */5 * * * ? |
Every 5 minutes |
| Staging | 0 0 3 * * ? |
Daily 03:00 IST |
| Production | 0 0 2 * * ? |
Daily 02:00 IST |
CRON Format: Seconds Minutes Hours Day Month DayOfWeek
src/
├── EmbaseConferenceScheduler.Domain/
│ ├── Entities/ # Business entities
│ ├── Interfaces/ # Repository & service contracts
│ └── Configuration/ # Settings models
│
├── EmbaseConferenceScheduler.Application/
│ └── Services/
│ └── PackagingService.cs # Core business orchestration
│
├── EmbaseConferenceScheduler.Infrastructure/
│ ├── Persistence/
│ │ └── ConferenceAbstractRepository.cs # Dapper + PostgreSQL
│ ├── FileTransfer/
│ │ └── SftpService.cs # SSH.NET SFTP
│ └── FileOperations/
│ └── ZipService.cs # ZIP creation
│
└── EmbaseConferenceScheduler.Worker/
├── Program.cs # Host bootstrap & DI
├── Jobs/
│ └── ConferenceAbstractPackagingJob.cs # Quartz job
├── Configuration/
│ ├── DependencyInjection.cs # Service registration
│ └── QuartzConfiguration.cs # Scheduler setup
└── appsettings.*.json # Environment configs
##Workflow Summary
tbldiscardeditemreport WHERE lotid NOT IN tblEmbaseConferenceDispatchSourceId (one ZIP per source)emconflumXXXXXXX.zip# Docker
docker logs -f embase-conference-scheduler
# Local
# Logs written to /logs/scheduler.log or C:/dev/embase/logs/ (Dev)
Job not running:
Database errors:
SFTP failures:
telnet sftp-host 22PDFs not found:
Packaging:PdfSourcePath is correctChange CRON to trigger every minute:
"Scheduler": {
"CronExpression": "0 * * * * ?"
}
Configure local Windows paths in appsettings.Development.json:
"Packaging": {
"PdfSourcePath": "C:/dev/embase/pdfs/",
"TempWorkingPath": "C:/dev/embase/tmp/"
}
"Serilog": {
"MinimumLevel": {
"Default": "Debug"
}
}
For detailed architecture documentation, see README_Architecture.md