Backend Data Model Relationships
This document describes the high-level data model relationships in the Processity.Backend project and provides Mermaid diagrams to visualize the core entity relationships (ERD) and module dependencies (tight and loose).
Scope & approach
- This is a conceptual, cross-module overview intended to help engineers understand how data flows between primary domains (CoreData, SocialCare, CareManagement, StaffManagement, CourseManagement, ProductData, Communications, EventManagement, Authorization, RuleEngine, etc.).
- The ERD shows core entities and key foreign-key relationships used across the backend modules.
- The dependency graph uses solid arrows for tight (strong, frequently accessed/owned) dependencies and dashed arrows for loose (integration / optional / eventual consistency) dependencies.
Canonical core entities (summary)
- Tenant: multi-tenant boundary; many entities have a
tenant_id. - User: authentication/account record mapped to staff or external users.
- Person: domain person (service user / citizen / client) and demographic data.
- Organization: provider organisations, teams, employers.
- Staff: employees, workers, with links to
UserandOrganization. - Case: social care case record that references
Personand assignedStaff. - Assessment: assessment records tied to a
CaseorPerson. - Appointment: scheduled meeting tied to
Case,Person,Staff. - CarePlan: a plan attached to a
Caseand composed ofTasks. - Task: actionable items, often assigned to
StafforTeams. - Course: training or course objects with
Enrollmentrecords. - Product/Order: product catalog and order/transaction records (Sales/ProductData).
- File: stored file metadata managed by FileService.
- Message/Communication: communication records (emails, SMS, in-app messages).
These canonical entities are referenced across multiple modules in the solution. Below we present a compact ERD and then a module dependency diagram.
ERD
The ERD below is a simplified, visual representation using colored boxes to show which module each entity belongs to. Each node lists the primary key and the key foreign key attributes used for cross-module relationships.
graph LR
%% --- Nodes (entity: multiline labels)
TENANT["TENANT\n(id PK)\nname"]
USER["USER\n(id PK)\ntenant_id FK\nusername\nemail"]
PERSON["PERSON\n(id PK)\ntenant_id FK\nfirst_name\nlast_name\nDOB"]
ORGANIZATION["ORGANIZATION\n(id PK)\ntenant_id FK\nname"]
STAFF["STAFF\n(id PK)\nuser_id FK\norganization_id FK\ntenant_id FK\nrole_id"]
CASE["CASE\n(id PK)\nperson_id FK\ntenant_id FK\nstatus"]
ASSESSMENT["ASSESSMENT\n(id PK)\ncase_id FK\nassessor_staff_id FK\ncreated_at"]
APPOINTMENT["APPOINTMENT\n(id PK)\ncase_id FK\nperson_id FK\nstaff_id FK\nstart/end"]
CAREPLAN["CAREPLAN\n(id PK)\ncase_id FK\ncreated_by_staff_id FK"]
TASK["TASK\n(id PK)\ncareplan_id FK\nassigned_staff_id FK\nstatus"]
COURSE["COURSE\n(id PK)\ntenant_id FK\ntitle"]
ENROLLMENT["ENROLLMENT\n(id PK)\ncourse_id FK\nuser_id FK"]
PRODUCT["PRODUCT\n(id PK)\ntenant_id FK\nsku"]
ORDER["ORDER\n(id PK)\nproduct_id FK\nperson_id FK"]
FILE["FILE\n(id PK)\ntenant_id FK\nowner_id FK\nowner_type"]
MESSAGE["MESSAGE\n(id PK)\nfrom_user_id FK\nto_user_id FK\nrelated_case_id FK"]
%% --- Styling: assign classes for modules
classDef core fill:#e8f4ff,stroke:#1f78b4,color:#0b3d5a
classDef staff fill:#e9f7ef,stroke:#2ca02c,color:#0b4d20
classDef social fill:#fff4e6,stroke:#ff8c00,color:#5a3a00
classDef course fill:#f7f0ff,stroke:#9b59b6,color:#3a1f4d
classDef product fill:#fff0f0,stroke:#d62728,color:#5a1515
class TENANT,USER,PERSON,ORGANIZATION core
class STAFF staff
class CASE,ASSESSMENT,APPOINTMENT,CAREPLAN,TASK social
class COURSE,ENROLLMENT course
class PRODUCT,ORDER product
class FILE,MESSAGE core
%% --- Relationships (annotated with FK attribute)
USER -->|tenant_id| TENANT
PERSON -->|tenant_id| TENANT
ORGANIZATION -->|tenant_id| TENANT
STAFF -->|user_id| USER
STAFF -->|organization_id| ORGANIZATION
CASE -->|person_id| PERSON
ASSESSMENT -->|case_id| CASE
APPOINTMENT -->|case_id / person_id / staff_id| CASE
CAREPLAN -->|case_id| CASE
TASK -->|careplan_id| CAREPLAN
TASK -->|assigned_staff_id| STAFF
ENROLLMENT -->|course_id| COURSE
ENROLLMENT -->|user_id| USER
ORDER -->|product_id| PRODUCT
ORDER -->|person_id| PERSON
FILE -->|tenant_id / owner_id| TENANT
FILE -->|owner_id| USER
MESSAGE -->|from_user_id / to_user_id| USER
%% Cross-link: staff assigned to cases (assignment table implied)
STAFF -->|"assigned_cases (join)"| CASE
%% Note: this layout intentionally trades ER-specific syntax for clearer, module-colored visualization.
Legend (module colors used in ERD)
graph LR
%% Legend for module colors used in the ERD above
LCore["CoreData"]:::core
LStaff["StaffManagement"]:::staff
LSocial["SocialCare"]:::social
LCourse["CourseManagement"]:::course
LProduct["ProductData"]:::product
classDef core fill:#e8f4ff,stroke:#1f78b4,color:#0b3d5a;
classDef staff fill:#e9f7ef,stroke:#2ca02c,color:#0b4d20;
classDef social fill:#fff4e6,stroke:#ff8c00,color:#5a3a00;
classDef course fill:#f7f0ff,stroke:#9b59b6,color:#3a1f4d;
classDef product fill:#fff0f0,stroke:#d62728,color:#5a1515;
%% Keep nodes separated for clarity
LCore --- LStaff
LStaff --- LSocial
LSocial --- LCourse
LCourse --- LProduct
Legend (project types in dependency graph)
graph LR
MModel["Model Project"]:::modelProject
MService["Service Project"]:::serviceProject
classDef modelProject stroke:#1f78b4,stroke-width:3px,fill:#fff,color:#000;
classDef serviceProject stroke:#2ca02c,stroke-width:3px,fill:#fff,color:#000;
MModel --- MService
Module Dependency Graph
The diagram below replaces the previous "Tight"/"Loose" labels with solid (tight/ownership) and dashed (loose/integration) edges. Each edge is annotated with the specific attributes or data flows that cause the dependency.
graph TB
%% Vertical layout (Top -> Bottom) with module-colored nodes
CoreData["CoreData\n(tenants, users, persons)"]
Authorization["Authorization\n(roles, permissions)"]
Staff["StaffManagement\n(staff, teams)"]
SocialCare["SocialCare\n(cases, assessments)"]
CareMgmt["CareManagement\n(careplans, tasks)"]
Course["CourseManagement\n(courses, enrollments)"]
Product["ProductData\n(products)"]
Sales["Sales\n(orders, invoices)"]
Comm["Communications\n(messages, notifications)"]
Event["EventManagement\n(events, calendar)"]
Rule["RuleEngine\n(business rules)"]
FileSvc["FileService\n(files, blobs)"]
%% Reuse the same class color definitions as the ERD so legend matches
classDef core fill:#e8f4ff,stroke:#1f78b4,color:#0b3d5a;
classDef staff fill:#e9f7ef,stroke:#2ca02c,color:#0b4d20;
classDef social fill:#fff4e6,stroke:#ff8c00,color:#5a3a00;
classDef course fill:#f7f0ff,stroke:#9b59b6,color:#3a1f4d;
classDef product fill:#fff0f0,stroke:#d62728,color:#5a1515;
class CoreData,Authorization,FileSvc core
class Staff staff
class SocialCare,CareMgmt social
class Course course
class Product,Sales product
class Comm core
class Event core
class Rule core
%% --- Project type classes: model vs service (visual distinction)
classDef modelProject stroke:#1f78b4,stroke-width:3px;
classDef serviceProject stroke:#2ca02c,stroke-width:3px;
%% Assign project-type classes (model projects own canonical entities)
class CoreData,Product,Course modelProject
%% Service projects (application/services that depend on models)
class Authorization,Staff,SocialCare,CareMgmt,Sales,Comm,Event,Rule,FileSvc serviceProject
%% --- Solid edges = ownership / strong coupling
CoreData -->|"tenant_id, user_id, canonical identities"| Authorization
CoreData -->|"user/person canonical ids, tenant_id"| Staff
CoreData -->|"person_id, tenant_id, user_id"| SocialCare
SocialCare -->|"case_id, person_id (case ownership)"| CareMgmt
Staff -->|"staff_id, team_id (assignments)"| CareMgmt
Product -->|"product_id, sku"| Sales
%% --- Dashed edges = loose/integration (events, optional refs)
SocialCare -.->|"case_id, person contact info (notifications)"| Comm
CareMgmt -.->|"task assignments, notifications"| Comm
Course -.->|"enrollments -> user_id, tenant_id"| CoreData
Sales -.->|"customer/person refs, tenant_id"| CoreData
Rule -.->|"case attributes, assessment data"| SocialCare
Rule -.->|"careplan and task data"| CareMgmt
FileSvc -.->|"file_id, owner_ref (entity id)"| SocialCare
FileSvc -.->|"file_id, owner_ref (staff profile)"| Staff
Event -.->|"calendar events, case_id"| SocialCare
Event -.->|"staff calendar, availability"| Staff
%% --- Cross-cutting event-driven integrations
SocialCare -.->|"events (case created/updated)"| Event
CareMgmt -.->|"events (task created/updated)"| Event
Sales -.->|"events -> notifications"| Comm
How to read the dependency graph
- Core modules like
CoreData,Authorization,StaffManagement, andSocialCareare tightly coupled because they own canonical records (users, tenants, persons, staff) and are referenced by many downstream modules. - Service integrations, notifications, file storage and rule evaluation are intentionally looser — they often rely on events or reference IDs rather than owning the canonical data.
Practical notes & recommendations
- Treat
CoreDataentities as canonical — avoid duplicating core identity data across subsystems. - Use event-driven patterns (EventBus) for loose integrations to keep strong ownership boundaries clear and reduce synchronous coupling.
- Where a module must reference another module's data (e.g.,
CareManagementreferencingStaff), prefer FK references toCoreDataIDs for integrity, and use denormalized snapshots for read performance when needed. - Document any module-specific extensions to the canonical entities inside that module's folder (e.g.,
SocialCare/srcshould documentCaseschema additions).
If you'd like, I can:
- generate a more detailed ERD for a specific module (e.g.,
SocialCare), - extract actual DB schema definitions from the repository and produce a physical ERD, or
- add cross-reference tables and sample queries showing how modules join on canonical entities.
Tell me which next step you'd prefer.