System Architecture

ADR: 0001 - System Architecture Style Selection

1. Context & Problem Statement

A Level 4 Hospital in Western Kenya requires an operational framework capable of handling high-concurrency workloads (such as registration, triage, labs, and pharmacies running simultaneously) while maintaining transaction integrity across multiple billing channels (Cash, SHA, and Corporate Insurance).

The infrastructure environment introduces specific operational challenges:

  • Electricity grids suffer from frequent power drops and brownouts.
  • Backhaul fiber connections experience unexpected cuts, making cloud-only deployments impractical.
  • The local IT engineering team is lean, meaning highly complex container orchestration setups (like Kubernetes) could introduce operational risks.

The system must guarantee clean modular separation between clinical domains to allow future scaling, while remaining simple enough to deploy and maintain on-premise using standard physical server hardware.

2. Considered Alternatives

To address these requirements, the architecture team evaluated three primary structural patterns:

Rendering Chart
  • Traditional (Unstructured) Monolith: A single codebase where all components share databases, logic blocks, and memory contexts directly.
  • Microservices Architecture: Deconstructing modules into completely independent, networked container systems communicating via REST, gRPC, or an external Message Bus.
  • Modular Monolith: A single deployment unit enclosing strictly isolated sub-applications (modules) with protected public interfaces and decoupled domain databases inside a unified relational engine

3. Decision Matrix & Evaluation Criteria

Evaluation DimensionTraditional MonolithMicroservices MeshModular Monolith (Selected)
Network Latency Overhead~0 ms (Direct In-Process)High (15-50 ms over HTTP/gRPC internal trunks)~0 ms (Direct In-Process Python Calls)
Local Deployment ComplexityLow (Single compose stack)Critical (Demands distributed state configuration)Low (Single Docker Compose file running on dual local nodes)
Domain Isolation ReinforcementPoor (Prone to structural spaghetti over time)Absolute (Enforced by network boundaries)High (Enforced by explicit Python internal module contracts)
Hardware Overhead RequirementsMinimalSubstantial (Each microservice requires dedicated resource allocation)Minimal (Shared memory runtime optimizing physical server cores)

4. Rationale for Selecting the Modular Monolith

The Modular Monolith was chosen because it effectively balances architectural isolation with operational simplicity for our environment.

4.1 Zero Network Overhead for Core Financial Loops

In microservices, checking if a patient has settled their bill before dispensing a medication requires a cross-network call between the Pharmacy Service and the Billing Service. If a local network switch fails or drops packets, that critical transaction stalls.

The modular monolith addresses this by executing cross-module calls directly in-memory via public Python interface methods. This pattern guarantees that billing and clinical lookups complete in fractions of a millisecond, completely bypassing the local network stack.

4.2 Code Maintainability without Infrastructure Complexity

By organizing the system into distinct Django applications with independent data models, the system prevents logical coupling. If the facility needs to transition a high-throughput module (such as the Laboratory Information System) into a standalone microservice in the future, the code boundaries are already established, allowing for an extraction with minimal rewriting.

4.3 Resilience to On-Premise Reality

Running a distributed microservices mesh on two physical servers in a regional hospital introduces significant operational overhead. A modular monolith simplifies local operations: the entire backend runs within a unified, highly optimized Python runtime, making deployment, logging, and troubleshooting straightforward for the on-site support team.

5. Consequences & Architectural Liabilities

While the modular monolith fits our core constraints, it introduces specific trade-offs that require systematic management:

  • Single Point of Failure (Runtime): A fatal error that crashes the primary Python process (such as a memory exhaustion event in a heavy report generator) can impact all modules. This risk is mitigated by offloading heavy computational tasks to background Huey workers.
  • Shared Database Resources: All modules ultimately persist data to the same PostgreSQL cluster. To prevent a long-running reporting query from blocking critical frontline database transactions, the system uses custom database schemas and resource-isolated read-replicas.
  • Monolithic Release Management: Deploying an update to a single module requires recycling the entire core application stack. The system manages this deployment window by automating rolling container restarts through our local reverse proxy layer.

ADR Sign-Off Block

Document Reference: ADR-0001-SYSTEM-STYLEApprover: Ian Wataka - backend developerDate Approved: June 22, 2026