Skip to content

Platform Architecture

Dharini Platform Architecture Diagram

This document summarizes the end-to-end architecture of Dharini, a disease surveillance and sample collection platform. It traces data flow from mobile field collection through laboratory processing to administrative dashboards.

The mobile app (Flutter/Dart) operates in an offline-first mode. Field collectors authenticate via OTP, select or create collection sites, and record visits with associated samples.

Collection Flow:

Login (OTP) → Site Selection/Creation → Visit Creation → Sample Collection → Submission
  1. Site Management: Collectors select nearby sites (cached locally) or create new ones with GPS coordinates. Sites link to projects and diseases under surveillance.

  2. Visit Creation: Each visit captures surveillance type, scheduled date, and site reference. Visits transition through statuses: UPCOMINGCOMPLETED.

  3. Sample Recording: For each sample, collectors:

    • Scan barcode (with optional replicates, up to 3 per sample)
    • Capture photos (compressed to 1MB, max 1920px)
    • Select sample type and associated diseases
    • Fill disease-specific questionnaires (configured per project)
    • Add comments
  4. Local Persistence: Data is stored in Hive boxes before sync:

    • pendingSubmissions: Queue of visits awaiting upload
    • completedVisits: Offline visit display cache
    • photoUploadMappings: Local path → S3 URL mappings
    • visitIdMap / siteIdMap: Local ID → server ID resolution
  5. Sync Pipeline: When online, SyncService executes:

    • Sync locally-created sites and visits (establishes server IDs)
    • Batch upload photos (groups of 3, concurrent)
    • Replace local paths with S3 URLs
    • Submit visit payload via POST /projects/{id}/submit-visit
    • Clean up local storage and invalidate query caches

The NestJS backend receives submissions and manages sample lifecycle through laboratory stages.

Sample Status Flow:

PICKED_UP → RECEIVED → PRE_PROCESSED → EXTRACTED → TESTED → TEST_REPORT_GENERATED
↓ (at any stage)
REJECTED

Each transition records the responsible user, timestamp, and stage-specific metadata (volumes, storage conditions, extraction kits, etc.).

Key API Endpoints:

EndpointPurpose
POST /projects/{id}/submit-visitMobile submission intake
GET /projects/{id}/samplesFiltered sample listing
PATCH /samples/{id}Status transition & metadata update
POST /samples/{id}/lab-testsCreate disease-specific tests
PATCH /samples/{id}/lab-testsRecord test results

Sample Pooling: Samples can be pooled for batch testing. When parent sample results are recorded, they propagate to pooled children automatically.

Audit Trail: SampleAudit entity captures all changes with user attribution and before/after snapshots.

Lab technicians use the Next.js web app to process samples through a 5-step accession pipeline.

Accession Stages:

  1. Receiving: Accept/reject samples, record received volume and storage location
  2. Pre-processing: Document homogenization, filtration, aliquoting steps
  3. Extraction: Select extraction kit, record volumes used and remaining
  4. Testing: Assign tests per disease, record Ct values and results (Positive/Negative/Unclear)
  5. Completion: Generate test reports, upload to S3

Bulk Processing: Lab staff can select multiple samples and process them through a stage simultaneously via the bulk processing modal.

Filtering & Export: Samples are filterable by disease, sample type, status, and date range. CSV export available for reporting.

Administrators access project-level analytics and management through the manage and monitor views.

Dashboard Components:

  • Statistics: Sample counts per pipeline stage, visualized as progress bars
  • Project KPIs: Projects grouped by disease, showing location coverage
  • Disease Monitoring: Site-wise sample breakdown with test result aggregation
  • User Management: Team assignments, role-based permissions

Access Control (CASL):

ScopeAccess Level
SystemAll projects (super admin)
ProjectSingle project (project admin)
TeamTeam data only (field staff, lab tech)

LayerTechnology
FrameworkFlutter 3.x, Dart 3.5
Stateflutter_hooks, fquery (react-query port)
Local DBHive (offline-first persistence)
HTTPDio via es_client (OpenAPI-generated)
Routinggo_router
AnalyticsPostHog, Sentry
Locationgeolocator
LayerTechnology
FrameworkNestJS 11, TypeScript
DatabasePostgreSQL 16 + PostGIS (TypeORM)
AuthJWT + Passport, OTP via SMS
StorageAWS S3 (LocalStack for dev)
QueueBullMQ (Redis-backed)
EmailNodemailer + Gmail SMTP
MonitoringPostHog, Sentry
LayerTechnology
FrameworkNext.js 15 (App Router), React 18
StateJotai (atomic state management)
HTTPAxios with JWT interceptor
StylingTailwind CSS
AuthhttpOnly cookies (24hr expiry)
ServiceProvider
DatabaseAWS RDS (PostgreSQL + PostGIS)
File StorageAWS S3
APK DistributionCustom Node.js server (/apk-server)
CI/CDGitHub Actions (mobile CI, smoke tests, deploy workflows)
Error TrackingSentry
AnalyticsPostHog
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Mobile │────▶│ Backend │────▶│ PostgreSQL │
│ (Flutter) │ │ (NestJS) │ │ + PostGIS │
└─────────────┘ └──────┬──────┘ └─────────────┘
┌─────────────────┼─────────────────┐
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│ S3 │ │ Redis │ │ Gmail │
│ (media) │ │ (queue) │ │ (email) │
└─────────┘ └─────────┘ └─────────┘
┌─────────────┐
│ Web │────▶ Backend API (same as mobile)
│ (Next.js) │
└─────────────┘


Document generated from production branch. Last updated: March 2026.