A distributed healthcare appointment management system built with .NET 9 and gRPC.
Doctors and patients manage medical appointments through a secure Razor Pages web client that communicates with a backend gRPC service over mTLS.
- Create schedules with working hours and slot durations
- Set validity periods and manage availability
- View and manage individual time slots
- Book appointments based on a doctor's schedule
- View upcoming and past appointments
- Cancel or reschedule appointments
- Appointment status tracking
- JWT-based authentication via gRPC
- Cookie authentication in the web client (8-hour expiry,
SameSite=Strict) - Antiforgery (CSRF) protection
- Security response headers (
X-Frame-Options,X-Content-Type-Options,Referrer-Policy)
| Layer | Technology |
|---|---|
| Runtime | .NET 9 / ASP.NET Core 9 |
| RPC | gRPC (Grpc.AspNetCore 2.70) |
| Web UI | Razor Pages |
| ORM | Entity Framework Core 9 |
| Database | SQL Server 2022 |
| Auth | ASP.NET Core Identity + JWT |
| Logging | Serilog (console + rolling file) |
| Observability | OpenTelemetry (traces + metrics) |
| Mapping | AutoMapper |
| Containers | Docker / Docker Compose |
┌──────────────────────────┐ gRPC/TLS ┌──────────────────────────┐
│ Medical.Client │ ──────────────────────► │ Medical.GrpcService │
│ (Razor Pages, :7082) │ │ (gRPC server, :7084) │
└──────────────────────────┘ └──────────┬───────────────┘
│ EF Core
┌──────────▼───────────────┐
│ SQL Server 2022 │
│ (:1433) │
└──────────────────────────┘
- .NET 9 SDK
- Docker Desktop or Rancher Desktop
- PowerShell 5.1+ (Windows) for certificate generation
# From the repository root — creates certs/server.pfx and certs/client.pfx
powershell -File makemedicalcerts.ps1To avoid browser certificate warnings, trust the generated root CA on your machine:
# Import the self-signed root CA into your Trusted Root store (run once)
$root = Get-ChildItem "cert:\CurrentUser\My" |
Where-Object { $_.Subject -eq "CN=MedicalRootCA" } |
Sort-Object NotBefore -Descending |
Select-Object -First 1
$store = New-Object System.Security.Cryptography.X509Certificates.X509Store("Root","CurrentUser")
$store.Open("ReadWrite")
$store.Add($root)
$store.Close()
Write-Host "Trusted: $($root.Thumbprint)"docker compose up -dThen open https://localhost:7082 in your browser.
# Terminal 1 — gRPC backend
dotnet run --project src/Medical.GrpcService
# Terminal 2 — web client
dotnet run --project src/Medical.ClientOpen https://localhost:7082.
| Role | Password | |
|---|---|---|
| Doctor | doctor@example.com | P@ssw0rd! |
| Patient | patient@example.com | P@ssw0rd! |
| Endpoint | Description |
|---|---|
GET /health/live |
Liveness — process alive |
GET /health/ready |
Readiness — DB reachable |
GET /health |
Aggregate |
| Variable | Service | Description |
|---|---|---|
Kestrel__Certificates__Default__Path |
both | Path to .pfx file |
Kestrel__Certificates__Default__Password |
both | PFX password |
ConnectionStrings__DefaultConnection |
GrpcService | SQL Server connection string |
Token__Key |
GrpcService | JWT signing key (≥ 512 bits) |
Token__Issuer |
GrpcService | JWT issuer URL |
Token__Audience |
GrpcService | JWT audience URL |
GrpcClient__BaseAddress |
Client | gRPC service base URL |
| Workflow | Trigger | Description |
|---|---|---|
| CI | push/PR to develop, master |
Build + unit tests |
| Docker Build | push/PR to develop, master |
Build both Docker images |
| CodeQL | push/PR + weekly schedule | Static security analysis |
MedicalApp/
├── src/
│ ├── Medical.GrpcService/ # gRPC backend (EF Core, JWT, OTel)
│ └── Medical.Client/ # Razor Pages frontend
├── tests/
│ └── Medical.GrpcService.Tests/
├── certs/ # Generated TLS certificates (git-ignored)
├── docker-compose.yml
├── makemedicalcerts.ps1 # Certificate generation script
├── Directory.Build.props # Shared MSBuild properties
└── Directory.Packages.props # Central NuGet package versions
Contributions are welcome. Please fork the repository and create a pull request targeting the develop branch.
Bohdan Harabadzhyu
.png)
.png)
.png)
.png)
.png)
.png)
.png)
.png)
.png)
.png)
.png)
.png)
.png)
.png)
.png)
.png)
.png)
.png)
.png)
.png)
.png)
.png)
.png)
