Product User Guide

Documentation and help for Medaius Clinic System

Patient Domain - Workflow Narrative

A conversational guide to understanding patient management workflows


What is the Patient Domain?

The Patient domain is the most foundational domain in the entire EHR system. Every clinical activity ultimately connects back to a patient. When you create an encounter, you're creating it for a patient. When you schedule an appointment, a patient is involved. When you prescribe medication, it's prescribed to a patient.

Think of the patient record as the central hub around which everything else orbits.


Creating a New Patient

Let's walk through what happens when a front desk staff member registers a new patient.

The Journey Begins

The user fills out the patient registration form on the frontend. They enter the patient's name, date of birth, gender, contact information, and any other demographic data. When they hit submit, a POST request goes to the /patients endpoint.

Validation First

Before anything gets saved, the system performs multiple layers of validation. The PatientCreateRequest model validates the data structure and types. Is the date of birth a valid date? Is the gender one of the allowed values? Is the patient name at least one character long?

If the patient has a custom patient ID (like a medical record number), the service checks if it's unique within the organization. Duplicate patient IDs would cause confusion, so this check is enforced strictly.

The Creation Process

Once validation passes, the PatientService.create_patient() method takes over. It extracts the organization ID from the authenticated user's context - patients always belong to an organization. If no custom patient ID was provided, the system generates one using the configured format from organization settings (like "PT-00001" or "MRN-2024-0001").

The service creates the Patient database record, sets the creation timestamp, and commits the transaction.

Activity Event Creation

Here's where things get interesting. After the patient is created successfully, the service fires off an activity event in a background thread. This happens asynchronously so the API response isn't delayed.

The event is created with type PATIENT_CREATED, priority low, and includes details like the patient name and who registered them. This event will appear in the organization's activity feed and could trigger notifications if configured.

Cache Invalidation

The patient list cache for this organization is invalidated. The next time someone requests the patient list, it will be fetched fresh from the database to include the new patient.

The Response

The API returns the created patient data with a 201 status code. The patient now exists in the system and can be used for scheduling appointments, creating encounters, and all other clinical workflows.


Retrieving Patient Data

The Basic Flow

When you request a patient by ID, the system does more than just fetch a database record. It enforces multi-tenant isolation and access control.

The PatientService.get_patient_by_id() method first checks if the patient belongs to the requesting user's organization. This is non-negotiable - you cannot access patients from other organizations even if you somehow know their IDs.

Access Control Lists

If the organization has ACL (Access Control Lists) enabled, the system goes further. It checks whether the requesting user has explicit permission to view this specific patient. The user might have a role that grants general patient access, or they might have a direct ACL grant from a clinical relationship (like having seen the patient as their practitioner).

Data Masking

Organizations can configure what happens when a user queries a patient they don't have full access to. In "basic" mode, the system returns just the essential identifying information - name, date of birth, patient ID. In "restricted" mode, even less is shown. Only users with proper access see the complete patient record.


Searching for Patients

The Intelligent Search Algorithm

Patient search is one of the most frequently used features, so it's heavily optimized.

When a user types "John Smith" into the search box, the PatientService.search_patients() method uses an intelligent search strategy. First, it looks for exact matches. If someone's name is exactly "John Smith", they'll be at the top of results.

If no exact matches are found, it falls back to case-insensitive matching, then prefix matching (names starting with "John"), then substring matching (names containing "John" anywhere).

For users who might misspell names, fuzzy search kicks in as a last resort. If the query is long enough (usually 4+ characters), the system uses PostgreSQL's trigram similarity to find phonetically similar names.

Multi-Field Search

The search doesn't just look at patient names. It also checks the patient ID field and can search within phone numbers and email addresses stored in the JSON telecom field. If a clinic uses local language names, those are searched too.

Performance Optimizations

Full-text search indexes on the patient name column make these searches fast even with hundreds of thousands of patients. Combined search conditions use OR logic - a patient matches if any of the search criteria match.

The results are always scoped to the user's organization and filtered by ACL if enabled. Pagination limits the result set to avoid overwhelming responses.


Updating Patient Records

Change Tracking

When you update a patient record, the system captures what changed and who changed it. The PatientService.update_patient() method accepts a PatientUpdateRequest with optional fields - only the fields you provide will be updated.

Before the update, the service fetches the current patient data. It then applies only the non-null fields from the update request. This partial update approach means you don't need to send the entire patient record just to change one field.

Activity Events for Updates

Just like creation, updates trigger activity events. The event includes what was changed (at a high level) and who made the change. This creates an audit trail - you can later see the history of modifications to a patient record.

Cascading Effects

Certain patient updates might have cascading effects. If you change the active status of a patient to inactive, that might affect their visibility in lists and their ability to have new appointments scheduled. The system handles these logical business rules within the service layer.


Aggregated Patient Statistics

Dashboard Statistics

The patient stats endpoint provides aggregated data for dashboards. Rather than fetching all patients and counting in Python (which would be slow and memory-intensive), the system uses database-level aggregation.

A single query with conditional aggregation counts total patients, active patients, new patients this month, and breaks down patients by gender. This approach works efficiently even with millions of records because the counting happens in the database.

ACL-Aware Statistics

If ACL is enabled, the statistics only include patients the requesting user has access to. A practitioner would see stats for their accessible patients, not the entire organization. This is both a privacy consideration and a practical one - showing a practitioner statistics for patients they can't even view would be misleading.


The Patient Summary

A Comprehensive View

The patient summary endpoint is one of the most complex operations. It pulls together data from multiple related tables to give a complete view of a patient's clinical status.

The PatientService.get_patient_summary() method fetches the patient along with their recent conditions, active medications, known allergies, recent observations, and current care team members. This involves multiple eager-loaded relationships to avoid the N+1 query problem.

What's Included

The summary includes:

  • Basic demographics
  • Active conditions with their diagnoses
  • Current medications with dosages
  • Known allergies with severity ratings
  • Recent vital signs (observations)
  • Upcoming and past appointments
  • Care team members and their roles

Use Cases

This summary powers the "patient at a glance" view that practitioners see when they open a patient's chart. It's also the data that feeds into AI summary generation - when the AI provides a patient overview, this is the source data it works from.


Deleting Patients

Soft Delete Preference

In a production medical system, true deletion is rare and often prohibited by regulations. Patient records need to be retained for legal and compliance reasons.

The delete_patient method typically performs a soft delete - setting is_active to False rather than removing the record. The patient becomes invisible in normal queries but the data remains for auditing and potential restoration.

Cascade Considerations

Even soft delete has implications. You need to decide what happens to related records. Do scheduled appointments get cancelled? Do pending encounters get closed? These business rules are implemented in the service layer based on organization requirements.


Connecting It All Together

The Patient domain doesn't exist in isolation. Here's how it connects to other domains:

To Encounters: Every encounter has a patient_id. You create encounters for patients. The patient record provides context for clinical documentation.

To Appointments: Appointments schedule time between patients and practitioners. The patient is one of the core participants.

To Billing: Invoices are generated for patients. The billing system aggregates charges across encounters for billing.

To Care Teams: Care teams are formed around patients. A care team exists for a specific patient and coordinates their care.

To Documents: Medical documents are attached to patients. Lab results, imaging, consent forms - they all link back to a patient record.

To Clinical Data: Allergies, medications, conditions, observations - all of these have a patient_id foreign key. The patient is the subject of all clinical data.


Key Takeaways

  1. Patients are central - they're the hub that connects nearly everything else

  2. Multi-tenancy is absolute - you can never access patients outside your organization

  3. ACL adds granularity - even within an organization, access can be restricted

  4. Activity events track changes - creation and updates are logged

  5. Search is optimized - multiple strategies ensure fast, relevant results

  6. Statistics use aggregation - database-level counting for performance

  7. Deletion is soft - records are retained for compliance


Next: Read about the Encounter Domain to understand how clinical visits are documented