Opened per visit
The encounter
- DocumentationNotes, diagnoses, vital signs
- OrdersPrescriptions, labs, imaging, consults
- CareAdmission, medication rounds, surgery
- ChargesEverything billed for the visit
Featured projects / Case study
A full system for a 100-bed general hospital, from the emergency room to the ward, the ICU, the theatre and the final bill, with a home screen built for each nurse and doctor.
Hospital software has to be strict where mistakes hurt people: who may approve a drug, who may see a patient, and whether anyone can quietly change the record afterwards.
One record per patient, shared by every department that treats them.
Orders that pass through a second, independent pair of hands.
Charges, insurance and stock that always reconcile.
A record of who did what that nobody can edit, not even an administrator.
The patient is the centre of the model. Every visit opens an encounter, and every clinical workflow hangs off it.
Kept for life
Allergies on every screen, the current stay, and an attention strip whose chips jump to the tab where the work is done. Wristband and barcode labels print from the header.
Opened per visit
A shift on the ward, from the nurse's first look to the audit trail that records it. Screenshots come from the running system.
1Nurses
Opens on what matters this shift: tasks overdue, beds, staffing against the ward ratio, and the patients who need attention now, most severe first.
2Nurses
Every dose, line re-site and STAT specimen, with its time and bed. A task turns red the minute it is due and leaves the list by itself once it is charted.
3Doctors
Critical results to acknowledge, red once they pass the laboratory target, results back in the last 24 hours, and the patients ranked by NEWS2 so the one who is deteriorating comes first.
4Doctors and nurses
The early-warning score is computed from the latest vital signs, including supplemental oxygen, and charted next to each sign so the cause of a rise is visible.
5Pharmacist
The doctor who wrote this morphine prescription tries to approve it. The API refuses and logs the attempt, even though the account holds both permissions.
6IT administrator
Each role is granted for a scope and a date range. A grant can start in the future or expire, and the change takes effect on the next request.
7ICU nurses
One row per hour for vital signs and NEWS2, with intake, output and a running fluid balance.
8Auditors and IT
Sign-ins, record views, clinical and financial changes and permission refusals, with who, what and where. Rows are append-only: a database trigger refuses any change.
The features a nurse or doctor reaches for every hour. Everything is derived from the chart on read, so nothing can drift out of date.
Ward dose times, with due and overdue doses and alerts
Beds, nurse per shift, attending doctor and staffing against the ratio
Scored from the latest vital signs, with trend charts
A red allergy band and MRN barcode stickers, printed from the chart
A pre-op checklist that gates the case start, the intra-op record and Aldrete recovery scoring
Tariffs by department, Lebanese payers with coverage shares, split between payer and patient
Ordered by the doctor, accepted by the receiving ward
A read-only interface for other hospital systems
Architecture
One deployable API, split into 15 modules whose dependencies point one way. Every request passes the same pipeline, then the checks of the endpoint it reaches.
Browser
HTTPS, with a 15-minute token that names the account and nothing else
API
Every request, in this order
15 modules, each with its own endpoints
Shared by every module
Writes and reads take different paths
Data
A least-privilege login at runtime
Database
Simple to run and test, with each module owning its endpoints and screens.
EF Core for the model, migrations and transactional writes. Stored procedures for every search and report.
Constraints and triggers enforce the rules no code path may skip.
A doctor wrote a prescription and now tries to approve it as the pharmacist. Their account holds both permissions. This is the path the request takes, and where it stops.
The token's account and its server-side session must still be open, and the person still employed.
401
order.medication.review at hospital scope, from the role assignments in force today, read from the database on every request.
403
The outcome must be one the pharmacy knows.
400
The reviewer must not be the prescriber. People are compared, not roles, so holding both roles does not help.
403
Only a new or active prescription can be reviewed.
409
The review, the new status, the audit row and any notification commit in one transaction.
200
Request
POST /api/prescriptions/{id}/review
Authorization: Bearer <token>
Content-Type: application/json
{ "outcome": "Approved" }
Response, abridged
HTTP/1.1 403 Forbidden
Content-Type: application/problem+json
{
"status": 403,
"title": "Not permitted",
"detail": "Separation of duties: the prescribing clinician must not also review the prescription.",
"correlationId": "…"
}
From the integration test A_prescriber_cannot_review_or_dispense_their_own_prescription, which then has a real pharmacist approve and dispense the same prescription.
An excerpt of the schema: the tables that request reads and writes. A role is granted for a scope and dates, and separation of duties compares two columns that point to the same person.
Scroll sideways to see the whole diagram.
The choices that shaped the system most.
01
02
By identity, not by role
03
04
What protects the data and keeps the system consistent.
15-minute tokens in a session with a 30-minute idle timeout, revoked when a password changes
Permission checked for a scope and a date range
Nobody approves their own order, result or purchase
Sign-ins, clinical and financial changes and permission refusals, uneditable
Five failures lock an account for 15 minutes
Row versions and locks stop double bookings and duplicate claims
Also: two-factor sign-in, server-side sessions that can be revoked, a least-privilege database login at runtime, sealed audit records and verified backups, PBKDF2 password hashing with forced change of temporary passwords, HTTPS, security headers, and no start-up with a weak signing key.
212 automated tests, wired into GitHub Actions: 184 backend tests (155 of them integration tests), 25 frontend unit tests and 3 browser journeys. Each integration run starts the real API against a fresh SQL Server database built from the real migrations and seed.
xUnit · 184 tests · 155 on real SQL Server
Sign-in, scopes, separation of duties, the search contract, medication schedules, NEWS2, the dashboards and the final bill. When two people act on one record at once, exactly one succeeds.
GitHub Actions
Warnings as errors, a check that migrations match the model, tests in a SQL Server container, then lint and build.
Vitest · 25 tests · Playwright · 3 journeys
The task and shift-clock rules are unit tested; sign-in, patient registration and critical-result acknowledgement run end to end in a real browser.
From .github/workflows/ci.yml. It runs on every push to the main branch and on every pull request. Each database it uses is a throwaway container that lives only as long as the job.
Ubuntu, .NET 10, a SQL Server 2022 container
Restore the packages and tools
Build, with every warning treated as an error
Check that the migrations match the model
Fail on any NuGet package with a published vulnerability
Run the 184 backend tests on a fresh database, with the API signed in as the least-privilege login. A missing server fails the run instead of skipping the tests.
Write the idempotent migration script for the database administrator and keep it as a build artifact
Ubuntu, Node 22
Install exactly what the lock file lists
Fail on any high-severity npm advisory
Lint
Run the 25 unit tests
Type-check and build
Only when both jobs pass:
Ubuntu, a SQL Server 2022 container, Chromium
Start the API: it migrates the empty database and seeds the sample hospital
Start the front end
Run the 3 Playwright journeys in a real browser
On a failure, keep the Playwright report and the API log
The frontend unit tests, with the same command the frontend job runs.
$ npm test
Test Files 5 passed (5)
Tests 25 passed (25)
b39a1db, with Vitest 5. The 184 backend tests and the 3 browser journeys are defined in the pipeline above, against its own SQL Server.A role means little without a scope and dates. Modelling that early saved the permission system from rewrites.
Rules that must never break belong in constraints and triggers too.
A written hospital profile, access rules and entity catalogue came first, then small verified batches.
The full system, its documentation and its test suite are complete, and it is now offered to hospitals.