Skip to content
Rapcsány Krisztián Rapcsány Krisztián
hu
← All projects Web business system

Dental practice management system

A system that runs the whole day-to-day of dental practices: appointments, treatment records, patients, cash desk, invoicing and regulatory reports. I am gradually rewriting the old system in Laravel, while it keeps running.

PHP Laravel 12 PostgreSQL Redis WebSockets SOAP integrations PDF and Excel
The challenge
My role

I work on the system, and I take part in rewriting the old version feature by feature.

A dental practice runs on many interdependent things: appointments, documenting treatments, invoicing, the cash desk, regulatory reports, patient communication. When those live in separate tools, the time goes into administration.

The task is to replace a system built over many years with a modern one, while the practices keep working. It is health data, so separating data and logging cannot be an afterthought.

The solution
01

Calendar and online booking

A scheduling diary with rooms and resources, online appointment booking, external calendar sync and automatic reminders.

02

Treatment records

Documenting treatments, periodontal status, lab sheets, templates, images and documents in the patient's record.

03

Patient files and portal

Master data, merging duplicate patients, import and export, and a patient portal where patients work with their own data and tasks.

04

Cash desk and invoicing

A cash desk with daily closing and multiple currencies, patient balances, and integration with an invoicing provider.

05

Regulatory integrations

Connection to the national health data system (e-prescriptions) and automated preparation of funder reports.

06

Communication and internal work

Newsletters, questionnaires, notifications, internal chat and a task manager, with reports in Excel and PDF.

Screenshots
Settings

The practice's system settings: log colours, warnings, invoicing, users and working hours.

Appointment calendar

A day view per chair, with colour-coded treatments. Patient names are blurred in the image.

With brand and customer names and personal data removed (except the app store screenshots).

What makes it special

Many practices, one database

Many practices share the same database, and every query is scoped to its own practice.

Field-level change log

Every modification is logged, so the handling of sensitive data can be checked at any time.

Patient portal and online booking

Patients work with their own data and tasks, and appointments can be booked online.

Challenges I ran into
01

The old behaviour has to be reproduced exactly

In a rewrite the goal is not to make it better, but to make it work the same, because the practices are used to it. So I move the old code over feature by feature, and keep a list of where the new one differs from the old.

02

A log that does not slow things down

The logging takes the original row from memory, so it needs no extra query, writes into monthly partitions, and runs through a queue so it does not slow the system.

03

Data must not mix

Since every practice sits in the same database, almost every query filters by the practice ID. The administrator can switch between practices, but has to ask for it explicitly.

Under the hood

Many practices, one system

A multi-tenant design: many practices share one database, and every query is strictly scoped to its own practice, so data cannot mix.

Gradual rewrite

The new system replaces the existing one feature by feature, reproducing behaviour one-to-one, so practices keep working throughout and there is no single risky cut-over.

Full traceability

A field-level change log for every modification, stored in monthly partitions and written through a queue, so handling of sensitive data can be audited at any time.

Real-time and reliable

WebSocket-based updates, background processing on Redis queues, a PostgreSQL database, and scheduled syncs with external systems.

Real code

A change log with no extra queries

Every modification of sensitive data is logged at field level. The original row comes from memory, so logging adds no database load and can be safely paused for bulk operations.

ChangeAuditor.php
/** Temporarily disable logging, e.g. during a bulk import or seeding. */
public static function withoutLogging(callable $callback): mixed
{
    $previous = self::$paused;
    self::$paused = true;

    try {
        return $callback();
    } finally {
        self::$paused = $previous;
    }
}

/** On `updating`, keep the original row in memory, so no extra SELECT is needed. */
public function captureOriginal(Model $model): void
{
    if (! $this->enabled($model)) {
        return;
    }

    $this->originals[spl_object_id($model)] = $model->getOriginal();
}
Result

One system that covers the whole practice, from the calendar to the cash desk and regulatory reports. Rollout is a one-day training plus transferring the existing data, and the system keeps renewing without interrupting the practices.

Is it time to rewrite or audit your old system?

It can be done while it runs, step by step, while keeping the behaviour.

More projects

Get a quote →