MediCare Clinic
A desktop clinic system where appointments, follow-up care and itemized invoices are held to database rules.
Context
- Role
- Backend: database, transactions, scheduling logic and tests
- Team
- Two people, with Sadia
- Duration
- 2026
- Stack
- Java 25, JavaFX, SQLite JDBC, JUnit 5, Maven
- Status
- Desktop build; public repository
MediCare is a desktop application for a small clinic front desk. It holds a patient directory, a doctor timetable, follow-up visits and an itemized billing ledger in Mauritian rupees. It runs on a local SQLite file, with no database server and no ORM.
I built the database layer, the invoice transaction, the scheduling rules and the test suite. The two-person team split matters: the work here is the state machine behind the screens, where a booking, a completed visit and a paid bill have to agree.
A bill is not a row. It is a transaction.
An invoice and its line items live in two tables. If the invoice is inserted and the items are not, the clinic has a bill with no charges. If the items are written without the invoice, they have no parent. Either failure is worse than a refused write, because it looks successful on screen.
A follow-up is a tree
A consultation can produce a follow-up, and that follow-up can produce another.parent_appointment_id points back into the same table. Billing needs a rollup across the whole chain, which means walking to the root and stopping safely. A cycle or an unbounded recursion would take the ledger down.
The calendar has rules, and the clock is one of them
The clinic opens at 08:00 and closes at 17:00, Monday to Friday. Consultations run from 15 to 120 minutes. A doctor cannot hold two overlapping appointments, a patient cannot hold two, a follow-up has to come after its parent, and a cancelled parent cannot spawn one. Testing any of that against the real clock is impossible on a weekend.
A bill is not a row. It is a transaction.
No ORM, on purpose
The project uses raw SQL over SQLite JDBC. That keeps the schema visible: foreign keys, check constraints and indexes are in one file, and each DAO maps rows explicitly. It also means the transaction boundary is written by hand, where it can be read.
Approach
The schema is the first specification
Five tables carry the system: patients, doctors, appointments, invoices and invoice items. Status values are constrained by CHECK, doctor rates are bounded between MUR 500 and 10,000, invoices have a unique appointment and a unique number, and five indexes cover the lookups the desk makes all day. Deleting a patient is restricted while appointments or unpaid invoices still reference them.
| patients | Contact details, date of birth and blood group; email unique |
|---|---|
| doctors | Specialty and hourly rate, bounded by a check constraint |
| appointments | Patient, doctor, time, status and the self-referential parent for follow-ups |
| invoices | One per appointment, with a unique invoice number and a payment status |
| invoice_items | Itemized charges, each tied to its invoice |
One write, two tables
Saving an invoice disables auto-commit, inserts the invoice, reads the generated key, inserts every item against it, then commits. Any SQL failure triggers a rollback and restores the connection's original auto-commit setting. The result is one atomic fact: either the bill and its charges exist, or neither does.
Six guards before a booking exists
Booking validation is a pipeline. Each guard returns a typed result, and the first failure stops the write with a reason the front desk can read.
| 1 · Fields | Patient, doctor, timestamp and duration must be present and in range |
|---|---|
| 2 · Hours | Monday to Friday, 08:00 to 17:00, with the end inside closing time |
| 3 · Future | The start cannot be in the past, measured against an injected clock |
| 4 · Doctor | No overlap with that doctor's active appointments; cancellations release the slot |
| 5 · Patient | No overlap with that patient's active appointments; rescheduling excludes itself |
| 6 · Follow-up | The parent exists, is not cancelled, and ends before the child begins |
Overlap detection uses the interval intersection rule: two appointments collide when each starts before the other ends. The check is symmetric for doctors and patients, and a reschedule passes its own ID so it does not conflict with itself.
A clock you can move
ScheduleValidator takes a java.time.Clock. Production passes the system clock; tests pass a fixed one. That single seam makes Monday morning, a Friday afternoon and a closed weekend reproducible in milliseconds. The validator is a plain class over an appointment DAO, so the tests use in-memory implementations and never touch the disk.
Recursion with an edge
The care-chain rollup walks parent links to the root, then summarizes every visit and its total. A dedicated stress test covers depth and cycle behavior. The goal is a ledger that fails predictably, not a stack overflow at the front desk.
Migrations you can replay
Startup reads PRAGMA user_version. Version zero runs the schema script, then records version one. Every statement is idempotent, so launching the app twice creates nothing twice. The packaged build carries its own Java runtime, so a clean machine can run the clinic without installing Java.
Shipped
The patient directory
Registration with contact details, date of birth and blood group. Search runs across name, phone and email. Deletion is blocked while a patient has an active appointment or an unpaid invoice, which keeps the ledger referentially honest.
The timetable
A doctor-and-date view with hourly slots from 08:00 to 17:00, each marked available, occupied or completed. A slot can be booked, completed or cancelled. A follow-up booking links to an earlier visit for the same patient, and the conflict guards run before the write. Completing a visit creates its invoice automatically.
The billing ledger
Pending and settled invoices, itemized charges, and payment through MCB Juice, card or cash. The ledger shows total billed, collected and pending amounts. Linked follow-ups appear under a treatment plan banner with the combined visit count and cost.
The supplied screenshots contained real patient names, phone numbers and dates of birth, so they are held outside the repository. The case is written from the schema, the source and the test run.
Evidence
5,491 lines of Java across 35 main source files and 11 test classes. Five tables, five indexes and a self-referential appointment tree.
Clock injection and in-memory DAOs. The scheduling tests are deterministic; they do not depend on the day or the disk.
BUILD SUCCESS. The packaged Windows build carries its own runtime and launches without a system Java installation.
Reflection
The visible product is a set of desktop screens. The actual product is the agreement between the timetable, the bill and the money. Most of my work went into making that agreement explicit enough to test.
The clock seam and the in-memory DAOs changed the tests from brittle setup into short scenarios. The invoice transaction changed the failure mode from a corrupt ledger to a refused write. Those two choices carry most of the reliability.
The next schema version would add a payments table. The dialog captures the payment method and reference, but the current invoice row only stores the status. A separate payment record would preserve the audit trail, and an optimistic version column would protect two front desks writing at once.
Make the invariant explicit, then make it impossible to skip.