Organization & Practitioner Domain - Workflow Narrative
A conversational guide to understanding multi-tenancy and provider management
What is Multi-Tenancy?
Multi-tenancy is the architectural pattern that allows a single system to serve multiple organizations in complete isolation. Clinic A and Clinic B share the same servers and database, but they never see each other's data.
This is foundational to the entire MedAIus platform. Nearly every table has an organization_id column, and nearly every query includes an organization filter.
Organizations
What an Organization Is
An organization represents a healthcare entity - a clinic, hospital, practice group, or health system. The Organization model includes:
- id - Unique identifier
- name - Display name
- slug - URL-friendly name
- type - hospital, clinic, practice_group
- is_active - Active status
- config - Organization-specific settings (JSON)
- subscription_tier - Plan level (basic, professional, enterprise)
Organization Types
- clinic - Single-location practice
- hospital - Larger facility with multiple departments
- practice_group - Multi-location practice under one umbrella
Configuration
The config JSON stores organization-specific settings:
- Branding (logo, colors)
- Feature toggles
- ACL configuration
- Appointment settings
- Encounter defaults
- And much more
This allows each organization to customize the system without code changes.
Clinics Within Organizations
Multi-Location Support
Large organizations may have multiple clinic locations. The Clinic model supports this:
- organization_id - Parent organization
- name - Clinic name
- address - Physical location
- contact info - Phone, email
- operating_hours - Business hours
Clinic Assignment
Practitioners can be assigned to clinics. This affects:
- Scheduling (which clinic's schedule?)
- Appointments (where is this appointment?)
- Reports (by location)
Organization Membership
Connecting Users to Organizations
The OrganizationMember junction table links users to organizations:
- user_id - The user
- organization_id - The organization
- role - Their role in this organization
- is_active - Current membership status
Multi-Org Users
A user can belong to multiple organizations. Dr. Smith might be a physician at Clinic A and a consultant at Clinic B. Their roles and access can differ per organization.
Context Switching
When a user is in multiple organizations, they select which one to work in. The frontend passes the organization_id, and the backend scopes all operations to that organization.
Practitioners
Healthcare Providers
The Practitioner model represents healthcare providers:
- id - Unique identifier
- user_id - Link to the user account
- organization_id - Which organization
- name - Professional name
- specialty - Medical specialty
- license_number - Professional license
- npi - National Provider Identifier
- status - active, inactive, on_leave
- qualifications - Degrees, certifications
Practitioner vs User
A user can have a practitioner profile. Not all users are practitioners - administrators, billing staff, and receptionists are users without practitioner profiles.
The practitioner profile adds clinical-specific data and enables features like scheduling and encounter attribution.
Practitioner Service Operations
Creating Practitioners
When creating a practitioner via PractitionerService.create_practitioner():
- Validate the user exists
- Validate the organization
- Check for duplicate profiles
- Create the Practitioner record
- Invalidate relevant caches
Practitioner Lookup
The service provides multiple lookup methods:
- By ID
- By user ID (get the practitioner for a user)
- By specialty (find cardiologists)
- By organization (all practitioners here)
- Search by name
Status Management
Practitioners have statuses:
- active - Currently practicing
- inactive - Not currently active
- on_leave - Temporarily unavailable
Status affects scheduling - inactive practitioners don't show available slots.
Schedules and Time Slots
Practitioner Schedules
Each practitioner has schedules defining their availability:
- practitioner_id - Which practitioner
- clinic_id - At which clinic
- day_of_week - Monday, Tuesday, etc.
- start_time/end_time - Working hours
- is_active - Is this schedule active?
Time Slots Within Schedules
Schedules contain time slots for granular booking:
- schedule_id - Parent schedule
- start_time/end_time - Slot boundaries
- slot_duration - In minutes
- max_bookings - Capacity per slot
- appointment_types - What can be booked here
Creating Schedules
PractitionerService.create_schedule() sets up availability:
- Validate the practitioner and clinic assignment
- Check for schedule conflicts
- Create the Schedule record
- Generate TimeSlot entries
Modifying Schedules
Schedule changes are carefully handled:
- Deleting a schedule with future appointments warns/prevents
- Time changes might invalidate existing appointments
- The service handles these edge cases
Clinic Assignment
Assigning Practitioners to Clinics
The ClinicAssignment model tracks which practitioners work at which clinics:
- practitioner_id - The practitioner
- clinic_id - The clinic
- start_date - When assignment started
- end_date - Optional end date
- is_primary - Primary work location?
Multi-Clinic Practitioners
A practitioner might work at multiple clinics. Their schedules are per-clinic. When checking availability, the system finds schedules across all assigned clinics.
Organization Configuration
Centralized Settings
OrganizationConfigService manages organization settings:
- Branding - Logo, app icon, colors
- Appointments - Buffer time, default duration
- Encounters - Default documentation type
- ACL - Access control settings
- Notifications - Default preferences
Getting Configuration
Services that need configuration call get_organization_config():
config = config_service.get_organization_config(organization_id)
buffer_time = config.appointments.buffer_time_minutes
Updating Configuration
Administrators can update configuration via update_organization_config(). The service:
- Validates the update payload
- Merges with existing config
- Saves to database
- Invalidates config cache
Section-Based Access
Configuration can be fetched by section:
get_config_section("branding")for just brandingget_config_section("appointments")for scheduling settings
This reduces data transfer when only specific settings are needed.
The /me Endpoint
User Context
The /me endpoint returns everything the current user needs:
- User profile
- Organization memberships
- Current organization details
- Practitioner profile (if applicable)
- Permissions
- Feature flags
Initial App Load
When the frontend loads, it calls /me to understand:
- Who is logged in
- What organizations are available
- What can this user do
Efficient Response
The MeService aggregates data from multiple sources into a single response, minimizing API calls during app initialization.
Statistics and Reporting
Practitioner Statistics
get_practitioner_stats() provides aggregated data:
- Total practitioners in organization
- Breakdown by specialty
- Breakdown by status (active/inactive)
- Gender distribution
Appointments by Practitioner
Who's busiest? Who has availability? These questions are answered by appointment statistics scoped to practitioners.
Key Takeaways
-
Organizations are tenants - Complete data isolation
-
Clinics are locations - Multiple sites per organization
-
Membership connects users - With roles per organization
-
Practitioners are providers - Users with clinical profiles
-
Schedules define availability - Per practitioner, per clinic
-
Configuration is flexible - JSON settings per organization
-
The /me endpoint - Everything needed at app start
Document created for the MedAIus EHR Platform