MediCare Clinic

A desktop clinic system where appointments, follow-up care and itemized invoices are held to database rules.

2026RepositoryTeam of two

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.

patientsContact details, date of birth and blood group; email unique
doctorsSpecialty and hourly rate, bounded by a check constraint
appointmentsPatient, doctor, time, status and the self-referential parent for follow-ups
invoicesOne per appointment, with a unique invoice number and a payment status
invoice_itemsItemized 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 · FieldsPatient, doctor, timestamp and duration must be present and in range
2 · HoursMonday to Friday, 08:00 to 17:00, with the end inside closing time
3 · FutureThe start cannot be in the past, measured against an injected clock
4 · DoctorNo overlap with that doctor's active appointments; cancellations release the slot
5 · PatientNo overlap with that patient's active appointments; rescheduling excludes itself
6 · Follow-upThe 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.

MediCare schema: patients, doctors, appointments, invoices and invoice items, with the atomic invoice write
The schema and the write boundary: appointments point at one invoice, invoices own their items, and the two inserts commit together.

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

57tests, zero failuresEleven JUnit classes, zero errors and zero skipped. Maven Surefire, 20 October 2026.
2tables in one transactionInvoice and line items commit together; any SQL failure rolls both back.
6scheduling guardsHours, future time, doctor overlap, patient overlap and follow-up continuity.

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.
NextAgent harness