Changes between Version 8 and Version 9 of ERModel


Ignore:
Timestamp:
09/14/26 21:45:47 (22 hours ago)
Author:
236021
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • ERModel

    v8 v9  
    1 = Conceptual Model =
    2 
    3 == ER Diagram ==
     1
     2= ER Diagram =
     3
     4== Diagram ==
    45
    56
     
    78
    89----
    9 = Data Requirements  =
    10 
    11 
    12 == Entities ==
     10== Data Requirements  ==
     11
     12=== Entities ===
    1313
    1414=== Doctor_Level ===
    15 * level_id (PK, bigint)
    16 * level (text)
     15A reference entity holding the set of rank levels a doctor can hold (e.g. Resident, Specialist, Consultant). Modeled as its own entity so the list of valid levels can be maintained centrally and referential integrity is enforced through the FK instead of relying on consistent free text.
     16
     17* '''Candidate keys:'''
     18
     19  level_id (surrogate)
     20
     21  level (natural, UNIQUE).
     22
     23`level_id` was chosen as PK because it is stable, renaming a level would otherwise cascade an update to every Doctors row.
     24
     25* '''Attributes:'''
     26
     27  level_id (PK, bigint) — required
     28
     29  level (text) — required, unique
    1730
    1831=== Doctor_Specialization ===
    19 * specialization_id (PK, bigint)
    20 * specialization_name (text)
     32Doctor_Specialization is an entity listing the medical specializations doctors can practice. It is kept separate from Doctors so Procedures can reference which specializations are allowed to perform them.
     33
     34* '''Candidate keys:'''
     35
     36  specialization_id (surrogate)
     37
     38  specialization_name (natural, UNIQUE).
     39
     40`specialization_id` was chosen as PK so a rename doesn't cascade into Doctors and Specialization_Procedures.
     41
     42* '''Attributes:'''
     43
     44  specialization_id (PK, bigint) — required; supplied by the application
     45
     46  specialization_name (text) — required, unique
    2147
    2248=== Departments ===
    23 * department_id (PK, bigint)
    24 * department_name (text)
     49Departments is an entity representing the clinic's organizational departments. Departments both employ doctors (works_in) and offer procedures (performs), so it needed to be a first-class entity.
     50
     51* '''Candidate keys:'''
     52
     53  department_id (surrogate)
     54
     55  department_name (natural, UNIQUE).
     56
     57`department_id` was chosen as PK so a rename doesn't cascade into Doctors and Department_Procedures.
     58
     59* '''Attributes:'''
     60
     61  department_id (PK, bigint) — required
     62
     63  department_name (text) — required, unique
    2564
    2665=== Doctors ===
    27 * doctor_id (PK, bigint)
    28 * first_name (text)
    29 * last_name (text)
    30 * email_address (text)
    31 * level_id (FK, bigint)
    32 * specialization_id (FK, bigint)
    33 * department_id (FK, bigint)
     66Doctors is the central entity for doctors who use the system to see patients, record diagnoses, order tests and perform procedures. Level, specialization and department are FKs to lookup entities rather than free text to prevent inconsistent values and allow filtering.
     67
     68* '''Candidate keys:'''
     69
     70  doctor_id (surrogate)
     71
     72  email_address (natural, UNIQUE).
     73
     74`doctor_id` was chosen as PK because an email address can change, and using a mutable value as PK would force cascading updates across every table referencing a doctor.
     75
     76* '''Attributes:'''
     77
     78  doctor_id (PK, bigint) — required
     79
     80  first_name (text) — required
     81
     82  last_name (text) — required
     83
     84  email_address (text) — required, unique, must contain an "@" (`LIKE '%@%'` - format check)
     85
     86  level_id (FK, bigint) — required, references Doctor_Level
     87
     88  specialization_id (FK, bigint) — required, references Doctor_Specialization
     89
     90  department_id (FK, bigint) — required, references Departments
    3491
    3592=== Patients ===
    36 * patient_id (PK, bigint)
    37 * first_name (text)
    38 * last_name (text)
    39 * email_address (text)
    40 * date_of_birth (date)
    41 * blood_type (text)
    42 * gender (text)
    43 * phone_number (text)
    44 * embg (text)
     93Patients represents the people receiving care, it is the anchor entity for almost all clinical data like appointments, diagnoses, medical records, tests, procedures, billing.
     94
     95* '''Candidate keys:'''
     96
     97  patient_id (surrogate),
     98
     99  embg (natural - the Macedonian unique master citizen number, required and unique).
     100
     101`patient_id` was still chosen as PK rather than embg: embg is sensitive personal data that is better not propagated as a foreign key into every dependent table (Diagnosis, Appointments, Medical_Records, etc.), and a numeric surrogate keeps those relationships smaller and independent of a nationally issued identifier.
     102
     103* '''Attributes:'''
     104
     105  patient_id (PK, bigint) — required
     106
     107  first_name (text) — required
     108
     109  last_name (text) — required
     110
     111  email_address (text) — optional, unique if provided
     112
     113  date_of_birth (date) — required
     114
     115  blood_type (text) — optional, restricted to one of: A+, A-, B+, B-, AB+, AB-, O+, O-
     116
     117  gender (text) — optional, restricted to one of: MALE, FEMALE
     118
     119  phone_number (text) — optional, free text
     120
     121  embg (text) — required, unique, 13-digit format
    45122
    46123=== Admin ===
    47 * admin_id (PK, bigint)
    48 * username (text)
    49 * name (text)
    50 * lastname (text)
    51 * email (text)
     124Represents clinic administrative staff who manage billing and back-office operations. Kept separate from Doctors and Lab_Technician since admins have no clinical attributes.
     125
     126* '''Candidate keys:'''
     127
     128  admin_id (surrogate),
     129
     130  username (natural, UNIQUE),
     131
     132  email (natural, UNIQUE).
     133
     134`admin_id` was chosen as PK so a username change doesn't cascade into Users and Billing.
     135
     136* '''Attributes:'''
     137
     138  admin_id (PK, bigint) — required
     139
     140  username (text) — required, unique
     141
     142  name (text) — required
     143
     144  lastname (text) — required
     145
     146  email (text) — required, unique, must contain "@adminmedora" (`LIKE '%@adminmedora%'` - restricts admins to the internal admin email domain)
    52147
    53148=== Lab_Technician ===
    54 * technician_id (PK, bigint)
    55 * username (text)
    56 * name (text)
    57 * lastname (text)
    58 * email (text)
     149Lab Technician entity is the lab staff who conduct the tests ordered by doctors and record results in Performed_Lab_Tests. Modeled separately from Doctors since technicians don't diagnose or prescribe.
     150
     151* '''Candidate keys:'''
     152
     153  technician_id (surrogate),
     154  username (natural, UNIQUE),
     155  email (natural, UNIQUE).
     156
     157`technician_id` was chosen as PK for the same reason as Admin.
     158
     159* '''Attributes:'''
     160
     161  technician_id (PK, bigint) — required, supplied by the application
     162
     163  username (text) — required, unique
     164
     165  name (text) — required
     166
     167  lastname (text) — required
     168
     169  email (text) — required, unique, must contain "@labmedora" (`LIKE '%@labmedora%'` - restricts technicians email domain)
    59170
    60171=== Users ===
    61 * user_id (PK, serial)
    62 * username (text)
    63 * password (text)
    64 * role (text)
    65 * first_name (text)
    66 * last_name (text)
    67 * patient_id (FK, bigint, nullable)
    68 * doctor_id (FK, bigint, nullable)
    69 * admin_id (FK, bigint, nullable)
    70 * technician_id (FK, bigint, nullable)
    71 * is_active (boolean)
     172Users is the authentication and login entity implementing role based access. A Users row is meant to serve exactly one of Patients, Doctors, Admin or Lab_Technician via whichever of the four nullable FKs is populated.
     173
     174* '''Candidate keys:'''
     175
     176  user_id (surrogate)
     177
     178  username (natural, UNIQUE).
     179
     180`user_id` was chosen as PK so a username change doesn't break session/audit data referencing the account. user_id is the one PK in this schema that is DB-generated (`SERIAL`).
     181
     182* '''Attributes:'''
     183
     184  user_id (PK, serial) — required, auto-generated by the database
     185
     186  username (varchar(255)) — required, unique
     187
     188  password (varchar(255)) — required, expected to be stored as a hash
     189
     190  role (varchar(50)) — required, intended to be one of patient/doctor/admin/technician
     191
     192  first_name (varchar(100)) — optional
     193
     194  last_name (varchar(100)) — optional
     195
     196  patient_id (FK, bigint, nullable) — references Patients
     197
     198  doctor_id (FK, bigint, nullable) — references Doctors
     199
     200  admin_id (FK, bigint, nullable) — references Admin
     201
     202  technician_id (FK, bigint, nullable) — references Lab_Technician
     203
     204  is_active (boolean) — optional, defaults to true
    72205
    73206=== Appointments ===
    74 * appointment_id (PK, bigint)
    75 * appointment_date (date)
    76 * appointment_time (time)
    77 * status (text)
    78 * patient_id (FK, bigint)
    79 * doctor_id (FK, bigint)
     207Appointments represents a scheduled meeting between one patient and one doctor. No natural key exists, so a surrogate key is mandatory.
     208
     209* '''Candidate keys:'''
     210
     211  appointment_id (surrogate) only.
     212
     213* '''Attributes:'''
     214
     215  appointment_id (PK, bigint) — required
     216
     217  appointment_date (date) — required
     218
     219  appointment_time (time) — required
     220
     221  status (text) — required, restricted to one of: SCHEDULED, COMPLETED, CANCELLED, IN_PROGRESS
     222
     223  patient_id (FK, bigint) — required, references Patients
     224
     225  doctor_id (FK, bigint) — required, references Doctors
    80226
    81227=== Diagnosis ===
    82 * diagnosis_id (PK, bigint)
    83 * name (text)
    84 * description (text)
    85 * patient_id (FK, bigint)
    86 * doctor_id (FK, bigint)
     228Diagnosis entity represents a diagnosis a doctor makes for a patient. It is the pivot the clinical model turns on since procedures, referrals and medical records all connect back to it.
     229
     230* '''Candidate keys:'''
     231
     232  diagnosis_id (surrogate) only.
     233
     234the same diagnosis name can legitimately recur for the same patient over time.
     235
     236* '''Attributes:'''
     237
     238  diagnosis_id (PK, bigint) — required; supplied by the application
     239
     240  name (text) — required
     241
     242  description (text) — optional
     243
     244  patient_id (FK, bigint) — required, references Patients
     245
     246  doctor_id (FK, bigint) — required, references Doctors
    87247
    88248=== Procedures ===
    89 * procedure_id (PK, bigint)
    90 * procedure_type (text)
    91 * procedure_date (date)
    92 * description (text)
    93 * cost (decimal)
    94 * doctor_id (FK, bigint)
    95 * diagnosis_id (FK, bigint)
     249Procedures is a catalog entry describing a type of procedure with its cost and the diagnosis it was primarily created for. Each row carries a direct diagnosis_id FK for its main diagnosis (1:N), while Diagnosis_Procedures (M:N) additionally lets the same catalog procedure be linked to other diagnoses without duplicating the row.
     250
     251* '''Candidate keys:'''
     252
     253  procedure_id (surrogate) only.
     254
     255procedure_type alone isn't unique, since the same type can be cataloged for different diagnoses at different costs.
     256
     257* '''Attributes:'''
     258
     259  procedure_id (PK, bigint) — required; supplied by the application
     260
     261  procedure_type (text) — required, must not be blank/whitespace-only
     262
     263  procedure_date (date) — required
     264
     265  description (text) — optional
     266
     267  cost (decimal) — required, must be ≥ 0
     268
     269  doctor_id (FK, bigint) — required, references Doctors
     270
     271  diagnosis_id (FK, bigint) — required, references Diagnosis
    96272
    97273=== Procedure_Results ===
    98 * result_id (PK, bigint)
    99 * result_description (text)
    100 * result_date (date)
    101 * procedure_id (FK, bigint)
     274Procedure_Results stores the outcome of a performed procedure. Kept separate from Procedures since one procedure can generate more than one result over time.
     275
     276* '''Candidate keys:'''
     277
     278  result_id (surrogate) only.
     279
     280* '''Attributes:'''
     281
     282  result_id (PK, bigint) — required
     283
     284  result_description (text) — optional, must not be blank
     285
     286  result_date (date) - required
     287
     288  procedure_id (FK, bigint) - required, references Procedures
    102289
    103290=== Prescriptions ===
    104 * prescription_id (PK, bigint)
    105 * medication_name (text)
     291Prescriptions is an entity for medications that can be prescribed. Kept minimal because everything patient specific like dosage, frequency, duration lives on Prescription_Medical_Records instead, since it varies per medical record.
     292
     293* '''Candidate keys:'''
     294
     295  prescription_id (surrogate) only.
     296
     297medication_name is required but has no UNIQUE constraint in this schema, so it is not a true candidate key as implemented.
     298
     299* '''Attributes:'''
     300
     301  prescription_id (PK, bigint) - required
     302
     303  medication_name (text) - required not enforced unique
    106304
    107305=== Prescription_Restriction ===
    108 * restriction_id (PK, bigint)
    109 * description (text)
    110 * prescription_id (FK, bigint)
     306Entity that records a restriction attached to a prescription. Separate entity because one prescription can carry multiple independent restrictions.
     307
     308* '''Candidate keys:'''
     309
     310  restriction_id (surrogate) only.
     311
     312* '''Attributes:'''
     313
     314  restriction_id (PK, bigint) - required
     315
     316  description (text) - required, must not be blank
     317
     318  prescription_id (FK, bigint) — required, references Prescriptions
    111319
    112320=== Lab_Tests ===
    113 * test_id (PK, bigint)
    114 * test_name (text)
    115 * description (text)
    116 * cost (decimal)
     321A catalog entity describing the lab tests the clinic offers, with standard cost. Separate from Performed_Lab_Tests, which records each actual instance of a test being run.
     322
     323* '''Candidate keys:'''
     324
     325  test_id (surrogate) only.
     326
     327
     328* '''Attributes:'''
     329
     330  test_id (PK, bigint) — required
     331
     332  test_name (text) — required, must be blank, not enforced unique
     333
     334  description (text) — optional
     335
     336  cost (decimal) — required, must be ≥ 0
    117337
    118338=== Lab_Results ===
    119 * result_id (PK, bigint)
    120 * results (text)
    121 * result_date (date)
    122 * test_id (FK, bigint)
     339Entity that stores the outcome of a catalog lab test, linked directly to the Lab_Tests catalog entry so the result stays reusable across reports (Medical_Report_Lab_Results, Medical_Record_Lab_Results).
     340
     341* '''Candidate keys:'''
     342
     343  result_id (surrogate) only.
     344
     345* '''Attributes:'''
     346
     347  result_id (PK, bigint) - required
     348
     349  results (text) - required, must not be blank
     350
     351  result_date (date) — required, must not be later than the current date
     352
     353  test_id (FK, bigint) — required, references Lab_Tests
    123354
    124355=== Medical_Records ===
    125 ''(simplified)''
    126 * record_id (PK, bigint)
    127 * patient_id (FK, bigint)
     356
     357Acts as a folder aggregating everything about a patient's clinical episode, by being the hub that other entities connect back to. Kept deliberately minimal so record specific detail lives on the appropriate junction table instead of being duplicated here.
     358
     359* '''Candidate keys:'''
     360
     361  record_id (surrogate) only.
     362
     363a patient can have multiple records over time, so patient_id alone isn't unique.
     364
     365* '''Attributes:'''
     366
     367  record_id (PK, bigint) — required
     368
     369  patient_id (FK, bigint) — required, references Patients
    128370
    129371=== Allergies ===
    130 * allergy_id (PK, bigint)
    131 * name (text)
    132 * allergy_severity (text)
     372An entity listing known allergies with a general severity classification, so the same allergy can be linked to many patients without re-entering its description each time.
     373
     374* '''Candidate keys:'''
     375
     376  allergy_id (surrogate)
     377
     378  name (natural, UNIQUE).
     379
     380`allergy_id` was chosen as PK so it survives a naming correction.
     381
     382* '''Attributes:'''
     383
     384  allergy_id (PK, bigint) - required
     385
     386  name (text) — required, unique, must not be blank
     387
     388  allergy_severity (text) - required, restricted to one of: LOW, MEDIUM, HIGH, CRITICAL
    133389
    134390=== Symptoms ===
    135 * symptom_id (PK, bigint)
    136 * name (text)
    137 * description (text)
     391A catalog entity listing known symptoms so the same symptom can be reused across many patients and diagnoses rather than typed freely each time.
     392
     393* '''Candidate keys:'''
     394
     395  symptom_id (surrogate)
     396
     397  name (natural, UNIQUE-enforced).
     398
     399`symptom_id` was chosen as PK for consistency with other catalog entities.
     400
     401* '''Attributes:'''
     402
     403  symptom_id (PK, bigint) — required
     404
     405  name (text) — required, unique, must be blank
     406
     407  description (text) — optional, if present, must not be blank
    138408
    139409=== Referrals ===
    140 * referral_id (PK, bigint)
    141 * reason (text)
    142 * referral_date (date)
    143 * record_id (FK, bigint)
    144 * from_doctor_id (FK, bigint)
    145 * to_doctor_id (FK, bigint)
     410Records one doctor referring a patient's medical record to another doctor. Carries two separate FKs to Doctors (from_doctor_id, to_doctor_id) because a referral inherently involves two distinct doctor roles.
     411
     412* '''Candidate keys:'''
     413
     414  referral_id (surrogate) only.
     415
     416* '''Attributes:'''
     417
     418  referral_id (PK, bigint) — required
     419
     420  reason (text) — required, must be blank
     421
     422  referral_date (date) — required
     423
     424  record_id (FK, bigint) — required, references Medical_Records
     425
     426  from_doctor_id (FK, bigint) — required, references Doctors
     427
     428  to_doctor_id (FK, bigint) — required, references Doctors
    146429
    147430=== Medical_Report ===
    148 * report_id (PK, bigint)
    149 * description (text)
    150 * report_date (date)
    151 * record_id (FK, bigint)
    152 * doctor_id (FK, bigint)
     431Represents a formal written report a doctor produces based on a medical record, which can reference multiple lab results as supporting evidence.
     432
     433* '''Candidate keys:'''
     434
     435  report_id (surrogate) only.
     436
     437a record can generate more than one report over time.
     438
     439* '''Attributes:'''
     440
     441  report_id (PK, bigint) — required; supplied by the application
     442
     443  description (text) — required
     444
     445  report_date (date) — required
     446
     447  record_id (FK, bigint) — required, references Medical_Records
     448
     449  doctor_id (FK, bigint) — required, references Doctors
    153450
    154451=== Billing ===
    155 * bill_id (PK, bigint)
    156 * total_cost (decimal)
    157 * payment_status (text)
    158 * payment_date (date)
    159 * record_id (FK, bigint)
    160 * admin_id (FK, bigint)
     452Represents an invoice generated for a medical record, covering whichever procedures/tests it includes, processed by an administrator. payment_date is nullable since a bill can be Pending before it's ever paid.
     453
     454* '''Candidate keys:'''
     455
     456  bill_id (surrogate) only.
     457
     458* '''Attributes:'''
     459
     460  bill_id (PK, bigint) — required
     461
     462  total_cost (decimal) — required, must be ≥ 0
     463
     464  payment_status (text) — required, restricted to one of: PENDING, PAID, CANCELLED
     465
     466  payment_date (date) — optional
     467
     468  record_id (FK, bigint) — required, references Medical_Records
     469
     470  admin_id (FK, bigint) — required, references Admin
    161471
    162472=== Performed_Procedures ===
    163 * performed_id (PK, bigint)
    164 * procedure_id (FK, bigint)
    165 * doctor_id (FK, bigint)
    166 * patient_id (FK, bigint)
    167 * diagnosis_id (FK, bigint, nullable)
    168 * procedure_date (date)
    169 * notes (text)
     473Records a specific, actual occurrence of a catalog procedure carried out on a patient. Distinct from Procedures, the catalog definition. diagnosis_id is nullable since a performed procedure isn't always tied to a newly recorded diagnosis.
     474
     475* '''Candidate keys:'''
     476
     477  performed_id (surrogate) only.
     478
     479* '''Attributes:'''
     480
     481  performed_id (PK, bigint) — required
     482
     483  procedure_id (FK, bigint) — required, references Procedures
     484
     485  doctor_id (FK, bigint) — required, references Doctors
     486
     487  patient_id (FK, bigint) — required, references Patients
     488
     489  diagnosis_id (FK, bigint, nullable) — references Diagnosis
     490
     491  procedure_date (date) — required
     492
     493  notes (text) — optional
    170494
    171495=== Performed_Lab_Tests ===
    172 
    173 * performed_test_id (PK, bigint)
    174 * test_id (FK, bigint)
    175 * patient_id (FK, bigint)
    176 * doctor_id (FK, bigint)
    177 * technician_id (FK, bigint)
    178 * test_date (date)
    179 * notes (text)
     496Records a specific, actual occurrence of a catalog lab test carried out on a patient that is ordered by a doctor, conducted by a technician.
     497
     498* '''Candidate keys:'''
     499
     500  performed_test_id (surrogate) only.
     501
     502* '''Attributes:'''
     503
     504  performed_test_id (PK, bigint) — required
     505
     506  test_id (FK, bigint) — required, references Lab_Tests
     507
     508  patient_id (FK, bigint) — required, references Patients
     509
     510  doctor_id (FK, bigint) — required, references Doctors
     511
     512  technician_id (FK, bigint) — required, references Lab_Technician
     513
     514  test_date (date) — required
     515
     516  notes (text) — optional
    180517
    181518=== Associative entities (M:N junctions that carry their own attributes) ===
    182519
    183520==== Prescription_Medical_Records ====
    184 * prescription_id (FK, bigint)
    185 * record_id (FK, bigint)
    186 * dosage (text)
    187 * frequency (text)
    188 * duration (text)
    189 * notes (text)
     521An associative (M:N) entity linking Prescriptions to Medical_Records. Carries its own attributes because dosage/frequency/duration are specific to the record a medication was prescribed within, not to the medication itself.
     522
     523* '''Candidate keys:'''
     524
     525  the composite (prescription_id, record_id) — a medication should appear at most once per record.
     526* '''Attributes:'''
     527
     528  prescription_id (FK, bigint) — required, part of composite PK, references Prescriptions
     529
     530  record_id (FK, bigint) — required, part of composite PK, references Medical_Records
     531
     532  dosage (text) — required
     533
     534  frequency (text) — required
     535
     536  duration (text) — required
     537
     538  notes (text) — optional
    190539
    191540==== Medical_Record_Symptoms ====
    192 * record_id (FK, bigint)
    193 * symptom_id (FK, bigint)
    194 * severity (text)
     541An associative (M:N) entity linking Medical_Records to Symptoms. Intended to carry a per-record severity, since how severe a symptom is is specific to that record/visit, not the symptom catalog entry.
     542
     543* '''Candidate keys:'''
     544
     545  the composite (record_id, symptom_id) - a symptom should appear at most once per record.
     546* '''Attributes:'''
     547
     548  record_id (FK, bigint) — required, part of composite PK, references Medical_Records
     549
     550  symptom_id (FK, bigint) — required, part of composite PK, references Symptoms
     551
     552  severity (text) — optional; no restricted value set enforced in the database
    195553
    196554==== Medical_Record_Allergies ====
    197 * record_id (FK, bigint)
    198 * allergy_id (FK, bigint)
    199 * reaction (text)
    200 * severity (text)
     555An associative (M:N) entity linking Medical_Records to Allergies. Intended to carry the reaction and severity observed for that patient within that specific record, distinct from the general allergy catalog entry.
     556
     557* '''Candidate keys:'''
     558
     559  the composite (record_id, allergy_id) — an allergy should appear at most once per record.
     560* '''Attributes:'''
     561
     562  record_id (FK, bigint) — required, part of composite PK, references Medical_Records
     563
     564  allergy_id (FK, bigint) — required, part of composite PK, references Allergies
     565
     566  reaction (text) — optional
     567
     568  severity (text) — optional
     569
    201570
    202571----
    203 
    204 == Relations ==
     572=== Relations ===
    205573
    206574=== is (Doctor_Level ↔ Doctors, 1:N) ===
     
    332700=== generates_bill (Medical_Records ↔ Billing, 1:N) ===
    333701a record can generate multiple bills, each bill is tied to one record.
     702