Skip to content

System Architecture

Applies to: All subscriptions

Purpose

Describe each component of a Complifly deployment: what it does, what it depends on, how it fails, and what that means for hosting it. This is the page an infrastructure team uses to plan servers, and a security team uses to enumerate trust boundaries.

Audience

Solution architects, infrastructure engineers, security reviewers.

Prerequisites

Reference

Components

Component Technology Role State held
Web front end React single-page application, built to static assets The user interface. Served as static files; all data comes from the API None. Token in browser storage
Application API Node.js with Express, default port 5003 Every business operation: ingest, validation, generation, administration, reporting None between requests
Background worker Node.js process Long-running and scheduled work: upload processing, write-back dispatch, retention, alerting Leases held in the database
Database Microsoft SQL Server System of record for compliance state, masters, configuration and audit. Enforces tenant isolation All of it
Queue Redis Job queue when queue-backed processing is enabled Transient job state
Reverse proxy IIS, nginx or a cloud load balancer TLS termination, security headers, routing static assets versus /api/* None

How they communicate

From To Protocol Notes
Browser Reverse proxy HTTPS Only public entry point
Reverse proxy Front-end assets file / HTTP Static bundle
Reverse proxy API HTTP on the internal network Terminate TLS at the proxy or re-encrypt per your standard
API Database TDS, default port 1433 Encrypted connection; never expose to the internet
API and worker Redis Redis protocol, default port 6379 Internal only; require authentication
API and worker GSP HTTPS out The only path to NIC and GSTN
API Mail transport SMTP or vendor HTTPS API Notifications, password reset, one-time codes
API Microsoft Entra ID HTTPS out Token signing keys, when machine-to-machine OAuth is in use
ERP API HTTPS in Ingest and write-back polling

Component behaviour and failure modes

Application API. Stateless: any instance can serve any request, so it scales horizontally behind a load balancer with no sticky sessions. It performs a schema check at startup and refuses to serve if a required database object is absent, naming the migration that creates it. Treat a refusal to start as correct behaviour; the alternative is a system that reports healthy and fails at the moment a user presses a button.

If the database is unreachable, the API does not start. If the GSP is unreachable, the API starts and every screen backed by local data keeps working — only operations that genuinely require the government fail. That asymmetry is deliberate and is the practical payoff of answering reads locally.

Background worker. Runs the work that must not block a request: upload job processing, write-back dispatch with retry and circuit breaking, retention and purge, and expiry alerting. Work is claimed with a database lease, so a worker that dies mid-task releases its claim when the lease expires rather than stranding the item.

Verify: Whether more than one worker instance may run concurrently against a single database is not established here. Until confirmed, plan for exactly one active worker. Recorded as assumption C4 in the Assumptions Register.

Database. The only stateful component and the one that decides your recovery objectives. Row-Level Security policies filter rows by tenant scope inside the engine. Backup, restore and disaster recovery are database concerns first; the application tier is disposable.

Redis. Required when queue-backed background processing is enabled. Its absence in that configuration is a silent failure mode worth designing against: the API answers normally, screens load, and background jobs simply never run. Monitor queue depth, not just the process. See Monitoring and Health Checks.

Trust boundaries

   Internet  |  DMZ / proxy tier  |  Application tier  |  Data tier
  -----------+--------------------+--------------------+-------------
   Browser   |   Reverse proxy    |  API instances     |  SQL Server
   ERP       |   TLS terminates   |  Worker            |  Redis
             |   here             |                    |
                                        |
                                        +---> outbound HTTPS to GSP,
                                              mail transport, Entra ID

Three boundaries a security reviewer will test:

  1. Internet to proxy. The only inbound path. Everything behind it should be unreachable from outside.
  2. Application to data. The database must not be internet-reachable. Use a dedicated least-privilege login; do not reuse an administrative account.
  3. Application to external services. Outbound only, to a small enumerable list. See Network and Firewall.

Process management

Application and worker processes are long-running Node.js services. They need a supervisor that restarts them on failure and on host reboot, and that keeps their logs. A process manager such as PM2 is used in reference deployments; a Windows service wrapper or a container orchestrator serves equally well. What matters is that all four properties hold: restart on crash, start on boot, log retention, and a documented way to stop cleanly for maintenance.

Validation

After deploying, confirm each component independently rather than concluding from one working screen:

Component Check Expected result
API GET /api/health through the proxy HTTP 200 with a success body. A 502 means the proxy cannot reach the API; a timeout means the API is not listening
API to database Sign in and open any list screen Data renders. If the API started at all, the database was reachable at startup — but confirm current connectivity, not only boot-time
Schema Application log at startup No missing-object message. If one appears, it names the migration to run
Worker Submit a file upload and watch the job Job moves from queued to complete. A job that never leaves queued means the worker or the queue is down, not the API
Redis, if enabled Queue depth metric A number that rises and falls. A depth that only rises means jobs are being enqueued and never consumed
Outbound The provider connection test in the admin console Succeeds. Failure here is a firewall or credential problem, not an application fault
Proxy Response headers on any page Security headers present; TLS version meets your standard

Troubleshooting

Symptom Cause Action
API will not start; log names a database object Startup schema guard found a required object missing Run the named migration, then restart. See Migrations
API will not start; no schema message Database unreachable, or credentials wrong Test the connection from the application host with the same credentials the service uses
Screens load but uploads never finish Worker down, or Redis unreachable while queue-backed jobs are enabled Check the worker process and queue depth. Restarting the API alone will not fix it
Everything works except government operations GSP unreachable or credentials expired Run the provider connection test. See GSP Configuration
Intermittent failures after adding a second API instance Sticky-session assumption, or two workers competing The API needs no stickiness; confirm the load balancer is not the cause, and confirm only one worker is active
PDF generation fails under load while everything else is fine Headless-browser rendering exhausted memory Raise the memory limit on the rendering host or reduce batch size. See Sizing and Capacity
Health endpoint returns 200 while users report failures The health probe is shallow by design Do not rely on it alone. Use the dependency checks in Health Endpoints