Case Study 01 · Full-Stack · Healthcare
EHR System
A full-stack Electronic Health Record system with role-based access, appointment scheduling, and PDF generation — built on Angular 19, .NET 9, PostgreSQL, and Redis. Cut API response times by ~40% through targeted caching.
The Problem
Healthcare platforms need to manage sensitive patient data, appointments, and records for multiple user roles — doctors, admin staff, patients — without sacrificing performance or security. I set out to build a system that handled all three properly: correct access control, fast under real load, and usable end to end, not just a CRUD demo.
My Role
End-to-end: architecture, backend API, database design, frontend, and deployment.
Architecture & Key Decisions
I structured the backend using Clean Architecture — a Domain layer holding core entities and business rules with no external dependencies, an Application layer defining use cases and interfaces, and an Infrastructure layer implementing those interfaces against PostgreSQL via EF Core. This kept business logic (appointment rules, access rules) testable and independent of the database and framework.
Authentication and authorization use JWT with role-based access control (RBAC) — doctors, admins, and patients each see and can act on a different slice of the system, enforced at the API layer via [Authorize(Roles = "...")].
fig · Clean Architecture layering
The Performance Problem
Early load testing showed read-heavy endpoints — doctor lookups, appointment listings — were slow under repeated requests, since every call hit PostgreSQL directly. I introduced Redis caching for frequently-read, rarely-changed data with sensible TTLs and cache invalidation on writes. This cut API response times by roughly 40% on those endpoints.
0%
faster API response
Before
~250 ms p50
After
~150 ms p50
Other Features
- Appointment scheduling and calendar views
- PDF generation for patient records and reports
- Responsive Angular 19 frontend consuming the API
What I'd Improve Next
Add integration tests around the RBAC boundaries, and explore read replicas or a queue-based write path if usage scaled further.