# Professional Management Platform ## Full REST-First System Design Plan > **Revision:** v3 — Implementation Architecture Baseline > **Status:** Engineering-MVP implementation baseline; production readiness still requires measured evidence, security review, and profession/jurisdiction-specific validation. > **Primary vertical:** Engineering > **API style:** REST + JSON + OpenAPI 3.1 > **Backend style:** Modular monolith > **Data:** PostgreSQL + profession-specific tables > **Tenant model:** Organization-scoped, explicit tenant context ### v3 Integration Notes v3 incorporates the strongest additions from the second design review while correcting several implementation traps. Added or strengthened: - webhook configuration, delivery history, retries, secret rotation, and signing - asynchronous import/export/report job model - engineering client contact management - project document-link APIs - design assignment, cancellation/withdrawal, and richer lifecycle rules - engineering project budget and budget-item modeling - document classification, retention, content hashing, and categories - refresh-token family lineage with separate session and token records - rate-limit policy framework without freezing arbitrary limits - property-based testing for state machines - chaos/reliability testing for transactional outbox consumers - operational and security metrics - explicit pre-production quality gates - expand/contract migration requirements - PostgreSQL 18 native UUIDv7 option - minimum viable file-policy framework - production checklist and observability requirements Corrected rather than copied literally: - refresh-token rotation does not create a schema conflict with `UNIQUE(refresh_token_family_id)` - webhook HMAC secrets are not stored as one-way hashes if the server needs them for signing - async export is modeled as a job resource rather than a side-effecting `GET` - inspection follow-up is modeled as an outcome/linked workflow rather than overloading inspection lifecycle state - document checksum is authoritative on document versions, not the parent document - document confidentiality is modeled as classification rather than a single boolean - nullable document-category uniqueness must use explicit PostgreSQL null semantics or partial indexes - budget spent/committed values must not become uncontrolled duplicate sources of financial truth - property-based tests distinguish valid and invalid transitions - outbox processing assumes at-least-once delivery and therefore requires idempotent consumers - tenant attack signals are distinct from internal tenant-isolation invariant failures - implementation phases are milestones, not a fictional calendar commitment - coverage percentage is a diagnostic metric, not a substitute for critical-path tests --- --- ## 2. Core Architecture Decision The platform will use: - REST - JSON - OpenAPI - Versioned endpoints - PostgreSQL - Modular monolith backend - Profession-specific frontends - Profession-specific database tables - Shared identity, security, billing, documents, audit, and infrastructure Base API path: ```text /api/v1 ``` GraphQL is not part of v1. --- ## 3. High-Level Architecture ```text FRONTENDS ┌──────────────────┼──────────────────┐ │ │ │ Engineering Web Legal Web Healthcare Web │ │ │ └──────────────────┼──────────────────┘ │ ▼ REST API /api/v1 │ ┌───────────┼───────────┐ │ │ │ Core Engineering Legal │ │ │ │ Healthcare │ │ │ │ └───────────┼───────────┘ │ PostgreSQL │ ┌───────────────┼────────────────┐ │ │ │ Shared Tables Profession Tables Audit/Event Tables ``` Shared infrastructure: ```text PostgreSQL Redis Object Storage Queue / Workers Audit Notifications Billing Observability ``` --- ## 4. System Architecture Strategy Start with a modular monolith. Do not start with microservices. Initial deployment: ```text Frontend Apps │ ▼ Backend API │ ├── PostgreSQL ├── Redis ├── Object Storage └── Worker Queue ``` Benefits: - simpler transactions - easier development - easier deployment - clearer domain boundaries - lower operational burden - easier refactoring - future service extraction remains possible --- ## 5. Repository Structure Recommended monorepo: ```text professional-platform/ │ ├── apps/ │ ├── engineering-web/ │ ├── legal-web/ │ ├── healthcare-web/ │ ├── platform-admin/ │ ├── api/ │ └── workers/ │ ├── packages/ │ ├── ui/ │ ├── api-client/ │ ├── auth-client/ │ ├── validation/ │ ├── types/ │ ├── config/ │ └── testing/ │ ├── database/ │ ├── migrations/ │ ├── seeds/ │ └── scripts/ │ ├── infrastructure/ │ ├── docker/ │ ├── deployment/ │ └── monitoring/ │ └── docs/ ├── architecture/ ├── api/ ├── security/ └── domains/ ``` --- ## 6. Frontend Strategy Every profession receives its own frontend application. Avoid one giant frontend filled with profession checks. ### Engineering Frontend Suggested navigation: ```text Dashboard Clients Projects Project Phases Project Team Sites Designs Design Reviews Inspections Specifications Tasks Documents Timesheets Billing Reports Administration ``` ### Legal Frontend Suggested navigation: ```text Dashboard Clients Matters Cases Hearings Courts Deadlines Documents Conflict Checks Time Tracking Retainers Billing Reports Administration ``` ### Healthcare Frontend Suggested navigation: ```text Dashboard Patients Appointments Practitioners Encounters Clinical Records Diagnoses Prescriptions Insurance Documents Billing Reports Administration ``` ### Platform Admin Frontend Suggested functions: ```text Organizations Users Profession Modules Subscriptions System Health Audit Support Global Configuration ``` Platform administrators and organization administrators are separate concepts. --- ## 7. REST API Structure Shared endpoints: ```text /api/v1/auth /api/v1/me /api/v1/organizations /api/v1/memberships /api/v1/membership-invitations /api/v1/roles /api/v1/permissions /api/v1/documents /api/v1/invoices /api/v1/payments /api/v1/audit-events ``` Engineering: ```text /api/v1/engineering/clients /api/v1/engineering/projects /api/v1/engineering/project-members /api/v1/engineering/phases /api/v1/engineering/sites /api/v1/engineering/tasks /api/v1/engineering/designs /api/v1/engineering/inspections /api/v1/engineering/specifications /api/v1/engineering/time-entries ``` Legal: ```text /api/v1/legal/clients /api/v1/legal/matters /api/v1/legal/cases /api/v1/legal/hearings /api/v1/legal/deadlines /api/v1/legal/conflict-checks /api/v1/legal/retainers /api/v1/legal/time-entries ``` Healthcare: ```text /api/v1/healthcare/patients /api/v1/healthcare/practitioners /api/v1/healthcare/appointments /api/v1/healthcare/encounters /api/v1/healthcare/clinical-records /api/v1/healthcare/diagnoses /api/v1/healthcare/prescriptions /api/v1/healthcare/insurance ``` --- ## 8. REST Conventions All APIs use JSON over HTTPS. Typical tenant-scoped request: ```http Authorization: Bearer X-Organization-Id: org_123 X-Request-Id: req_123 Content-Type: application/json ``` ### Organization Context `X-Organization-Id` is mandatory for every tenant-scoped endpoint. It is deliberately explicit even when a user currently belongs to only one organization. Silent organization selection creates ambiguous clients and becomes dangerous the moment the user later joins a second organization. Global endpoints such as these do not require tenant context: ```http POST /api/v1/auth/login POST /api/v1/auth/token/refresh GET /api/v1/me GET /api/v1/me/organizations GET /api/v1/auth/sessions ``` Tenant-context resolution rules: ```yaml Organization Context: header_missing_on_tenant_endpoint: status: 400 code: ORGANIZATION_CONTEXT_REQUIRED organization_not_found: status: 404 code: RESOURCE_NOT_FOUND membership_not_found: status: 404 code: RESOURCE_NOT_FOUND membership_inactive: status: 403 code: AUTHZ_MEMBERSHIP_INACTIVE organization_inactive: status: 403 code: AUTHZ_ORGANIZATION_INACTIVE resource.organization_id_mismatch: status: 404 code: RESOURCE_NOT_FOUND ``` Do not return another organization's name or membership details in tenant-error responses. ### Idempotency Use: ```http Idempotency-Key: 8f7d6c5e-4b3a-2b1c-9d8e-7f6a5b4c3d2e ``` Idempotency is required for commands where duplicate execution can create financial, regulated, external, or otherwise material side effects. Examples: ```http POST /api/v1/invoices POST /api/v1/invoices/{id}/payments POST /api/v1/payments/{id}/refund POST /api/v1/engineering/designs/{id}/approve POST /api/v1/engineering/inspections/{id}/complete POST /api/v1/legal/retainers POST /api/v1/legal/conflict-checks/{id}/approve POST /api/v1/healthcare/prescriptions POST /api/v1/healthcare/clinical-records/{id}/sign ``` Do not require idempotency on every ordinary PATCH by default. Idempotency records must include: ```text organization_id actor_id route/action idempotency_key canonical_request_hash response_status response_body or resource reference created_at expires_at ``` Rules: ```yaml Same key + same operation + same request hash: return: original result Same key + different request hash: status: 409 code: IDEMPOTENCY_KEY_CONFLICT ``` Durability: - PostgreSQL is authoritative for critical idempotency records. - Redis may cache recent records for speed. - Redis eviction must not make a payment or regulated command executable twice. - Downstream providers should receive their own idempotency key where supported. ## 9. Standard Response Format Single resource: ```json { "data": { "id": "project_123", "name": "Central Tower" } } ``` Collection: ```json { "data": [], "meta": { "pagination": { "nextCursor": null, "hasMore": false } } } ``` Standard error: ```json { "error": { "code": "RESOURCE_NOT_FOUND", "message": "Resource not found.", "details": {}, "requestId": "req_123" } } ``` Clients depend on `error.code`, not message text. ### Error Taxonomy Authentication: ```text AUTH_INVALID_CREDENTIALS AUTH_TOKEN_EXPIRED AUTH_TOKEN_INVALID AUTH_MFA_REQUIRED AUTH_SESSION_REVOKED AUTH_REFRESH_TOKEN_REUSED ``` Authorization: ```text AUTHZ_PERMISSION_DENIED AUTHZ_ORGANIZATION_INACTIVE AUTHZ_MEMBERSHIP_INACTIVE AUTHZ_CREDENTIAL_INVALID AUTHZ_SCOPE_MISMATCH ``` Tenant context: ```text ORGANIZATION_CONTEXT_REQUIRED ``` Resource/state: ```text RESOURCE_NOT_FOUND RESOURCE_ALREADY_EXISTS RESOURCE_CONCURRENT_MODIFICATION RESOURCE_INVALID_STATE RESOURCE_ARCHIVED ``` Validation: ```text VALIDATION_ERROR VALIDATION_REQUIRED_FIELD VALIDATION_INVALID_FORMAT VALIDATION_BUSINESS_RULE ``` Idempotency: ```text IDEMPOTENCY_KEY_REQUIRED IDEMPOTENCY_KEY_CONFLICT ``` Rate limiting: ```text RATE_LIMIT_EXCEEDED ``` System/dependency: ```text INTERNAL_ERROR SERVICE_UNAVAILABLE DATABASE_UNAVAILABLE DEPENDENCY_FAILED ``` Validation example: ```json { "error": { "code": "VALIDATION_ERROR", "message": "Request validation failed.", "requestId": "req_123", "details": { "fields": [ { "field": "email", "code": "INVALID_FORMAT", "message": "Must be a valid email address" } ] } } } ``` Business-state example: ```json { "error": { "code": "RESOURCE_INVALID_STATE", "message": "Cannot approve design in current state.", "requestId": "req_123", "details": { "resourceType": "engineering_design", "resourceId": "design_123", "currentState": "draft", "requiredState": "under_review", "allowedActions": [ "submit_review" ] } } } ``` Do not expose internal stack traces, SQL, policy internals, secrets, or cross-tenant information. ## 10. HTTP Status Rules ```text 200 Success 201 Created 202 Accepted 204 No Content 400 Bad Request 401 Unauthorized 403 Forbidden 404 Not Found 409 Conflict 422 Validation Error 429 Too Many Requests 500 Internal Server Error ``` Cross-tenant resource access should return 404. --- ## 11. API Versioning Current API: ```text /api/v1 ``` Breaking changes require: ```text /api/v2 ``` Additive fields generally do not require a new version. --- ## 12. Authentication Initial authentication: ```text Email + Password + Short-Lived Access Token + Opaque Refresh Token + Server-Side Session ``` REST endpoints: ```http POST /api/v1/auth/register POST /api/v1/auth/login POST /api/v1/auth/token/refresh POST /api/v1/auth/token/revoke POST /api/v1/auth/token/revoke-all GET /api/v1/auth/sessions DELETE /api/v1/auth/sessions/{sessionId} GET /api/v1/me ``` ### Access Token ```yaml format: JWT lifetime: 15 minutes by default signed: true encrypted: false preferred signing: asymmetric key or managed signing service claims: - sub / userId - sessionId - issuer - audience - issuedAt - expiresAt organizationId: optional_hint: true authorization_authority: false ``` The organization header and active membership remain authoritative for tenant access. Do not embed the complete permission set in access tokens. ### Session and Refresh-Token Model A login session and a refresh token are different resources. Use: ```text sessions refresh_tokens ``` Suggested `sessions` fields: ```text id user_id device_id device_type device_os app_version ip_address user_agent created_at last_active_at expires_at revoked_at revocation_reason ``` Suggested `refresh_tokens` fields: ```text id session_id family_id token_hash issued_at expires_at rotated_at replaced_by_token_id revoked_at revocation_reason ``` Indexes/constraints: ```text UNIQUE(refresh_tokens.token_hash) INDEX(refresh_tokens.family_id) INDEX(refresh_tokens.session_id) INDEX(sessions.user_id, sessions.revoked_at) ``` Do **not** make `family_id` unique. Every rotated refresh token in the same lineage shares the same family. Conceptually: ```text Session │ └── Refresh Token Family │ ├── Token A [rotated] │ ↓ ├── Token B [rotated] │ ↓ └── Token C [current] ``` ### Refresh Rotation On successful refresh: 1. hash supplied refresh token 2. load token and session 3. validate token/session status and expiry 4. issue replacement token in same family 5. mark old token rotated 6. link `replaced_by_token_id` 7. return new access + refresh tokens ### Reuse Detection If a previously rotated token is used again: ```text possible token theft ↓ revoke token family ↓ revoke affected session ↓ security audit event ↓ reauthentication required ``` Policy may escalate to revoking all user sessions for higher-risk environments. Audit event: ```text auth.refresh_token.reuse_detected ``` ### Device Metadata Device metadata is useful for: ```text session display security alerts audit context user-initiated revocation ``` It is not identity proof. Future authentication: - MFA - passkeys / WebAuthn - OIDC / SSO - enterprise identity providers - risk-based authentication ## 13. Shared Core Backend Recommended modules: ```text core/ ├── auth/ ├── users/ ├── organizations/ ├── memberships/ ├── roles/ ├── permissions/ ├── authorization/ ├── documents/ ├── billing/ ├── notifications/ ├── audit/ └── events/ ``` Dependency rule: ```text Profession module → Core ``` Never: ```text Core → Profession module ``` --- ## 14. Organizations Organizations are tenants. Examples: ```text Atlas Structural Engineering Smith & Associates Law North Shore Medical Practice ``` Suggested fields: ```text id name slug status country_code timezone currency_code created_at updated_at ``` --- ## 15. Profession Enablement Use: ```text organization_professions ``` Suggested fields: ```text organization_id profession enabled_at configuration ``` Possible professions: ```text engineering legal healthcare ``` An organization may eventually enable more than one profession module. --- ## 16. Users and Memberships Users are global identities. A user gains tenant access through membership. ```text User │ ▼ Membership │ ▼ Organization ``` Suggested `users` fields: ```text id email first_name last_name phone avatar_url status created_at updated_at ``` Suggested `memberships` fields: ```text id organization_id user_id status joined_at created_at updated_at ``` --- ## 17. Membership Invitations Keep invitations separate from memberships. Suggested table: ```text membership_invitations ``` Fields: ```text id organization_id email invited_by_user_id expires_at accepted_at revoked_at created_at ``` Flow: ```text Invitation ↓ Accepted ↓ User ↓ Membership ``` --- ## 18. Authorization Use: ```text RBAC + Permission Scope + Resource Policies + Professional Qualification Policies + Domain State Rules ``` Decision flow: ```text Authenticated User ↓ Explicit Organization Context ↓ Active Membership ↓ Enabled Profession Module ↓ Roles ↓ Permissions ↓ Permission Scope ↓ Tenant-scoped Resource Query ↓ Resource Policy ↓ Credential/Jurisdiction Policy ↓ Domain State Rule ↓ ALLOW / DENY ``` Default decision: ```text DENY ``` Authorization rules: 1. Controllers never perform ad-hoc role comparisons. 2. Tenant resource queries always include `organization_id`. 3. Do not load an arbitrary resource first and then discover it belongs to another tenant. 4. High-risk professional actions perform credential checks at command execution time. 5. A permission grants the ability to attempt an action, not a guarantee the domain state allows it. 6. Cross-tenant resources appear nonexistent. 7. Profession module enablement is checked before profession-specific authorization. ## 19. Roles and Permissions Roles are organization-scoped collections of permissions. Example roles: ```text Owner Administrator Project Manager Engineer Reviewer Inspector Lawyer Paralegal Doctor Nurse Billing Manager Viewer ``` Roles are not professional credentials. ### Engineering Permissions ```text engineering.clients.read engineering.clients.create engineering.clients.update engineering.clients.archive engineering.projects.read engineering.projects.create engineering.projects.update engineering.projects.activate engineering.projects.close engineering.projects.archive engineering.project_members.manage engineering.phases.manage engineering.tasks.manage engineering.sites.manage engineering.documents.read engineering.documents.upload engineering.documents.delete engineering.designs.read engineering.designs.create engineering.designs.update engineering.designs.review engineering.designs.approve engineering.designs.reject engineering.designs.supersede engineering.inspections.read engineering.inspections.manage engineering.inspections.complete engineering.time_entries.manage engineering.reports.read ``` ### Legal Permissions ```text legal.clients.read legal.clients.create legal.clients.update legal.matters.read legal.matters.create legal.matters.update legal.matters.close legal.matters.reopen legal.cases.read legal.cases.manage legal.hearings.manage legal.deadlines.manage legal.documents.read legal.documents.upload legal.conflicts.manage legal.conflicts.approve legal.retainers.manage legal.time_entries.manage ``` ### Healthcare Permissions ```text healthcare.patients.read healthcare.patients.create healthcare.patients.update healthcare.appointments.read healthcare.appointments.manage healthcare.encounters.read healthcare.encounters.manage healthcare.records.read healthcare.records.write healthcare.records.sign healthcare.records.amend healthcare.records.access_log.read healthcare.prescriptions.read healthcare.prescriptions.write healthcare.prescriptions.sign healthcare.insurance.read healthcare.insurance.manage ``` ### Shared Permissions ```text documents.read documents.upload billing.read invoices.create invoices.issue invoices.void payments.record payments.refund members.read members.invite members.update members.remove roles.read roles.manage audit.read ``` Avoid vague permissions such as `admin_everything` in normal tenant RBAC. ## 20. Permission Scopes Initial scopes: ```text assigned organization ``` Examples: ```text Engineer: engineering.projects.read = assigned Principal Engineer: engineering.projects.read = organization ``` Potential future scopes: ```text owned team department restricted ``` Do not implement until required. --- ## 21. Professional Credentials Professional qualification is separate from RBAC. Suggested shared profile: ```text professional_profiles ``` Fields: ```text id organization_id user_id profession title credential_status primary_license_number primary_license_jurisdiction valid_from expires_at created_at updated_at ``` Profession modules may add dedicated credential tables when one generic profile is insufficient. ### Credential Policy Examples Engineering design approval may require: ```yaml permission: engineering.designs.approve credential: profession_family: engineering status: verified active_license: true jurisdiction_match: when required discipline_match: when required ``` Healthcare record signing may require: ```yaml permission: healthcare.records.sign credential: profession_allowed_by_policy: true status: verified active_license: true scope_of_practice_allows_action: true jurisdiction_match: true ``` Prescribing must **not** be hard-coded to `profession = doctor` or to a single U.S. credential such as a DEA number. Prescribing authority varies by: - jurisdiction - profession - drug class - supervising relationship - organization policy - credential status Therefore use a policy concept such as: ```text PrescribingAuthorityPolicy ``` rather than a permanent global rule. ### Cache Safety Credential status may be cached briefly for ordinary reads, but high-risk writes such as: ```text engineering.designs.approve healthcare.records.sign healthcare.prescriptions.sign ``` must use authoritative or revocation-aware credential validation. A five-minute stale cache is unacceptable if a license was just suspended. ## 22. Database Architecture Use PostgreSQL. Start with: ```text One database + Shared schema + Profession-specific tables ``` Do not begin with database-per-profession or database-per-customer unless compliance or residency requirements force that choice. --- ## 23. Shared Tables Recommended shared tables: ```text organizations organization_professions users user_credentials sessions memberships membership_invitations roles permissions role_permissions membership_roles professional_profiles documents document_versions invoices invoice_items payments notifications notification_deliveries audit_events outbox_events ``` --- ## 24. Multi-Tenancy Rule Every tenant-owned row must contain: ```text organization_id ``` Examples: ```text engineering_projects.organization_id legal_matters.organization_id healthcare_patients.organization_id ``` Enforce tenant boundaries at: - API layer - authorization layer - repository/query layer - database constraints --- ## 25. Tenant-Safe Foreign Keys Use composite tenant-aware foreign keys when possible. Example: ```text engineering_projects organization_id client_id ``` references: ```text engineering_clients organization_id id ``` This prevents linking a resource from one organization to another organization's data. --- # Engineering Domain ## 26. Engineering Tables Initial tables: ```text engineering_clients engineering_projects engineering_project_members engineering_project_phases engineering_sites engineering_tasks engineering_designs engineering_design_versions engineering_design_reviews engineering_inspections engineering_inspection_findings engineering_specifications engineering_change_requests engineering_time_entries ``` --- ## 27. Engineering Clients Suggested core client fields: ```text id organization_id client_type display_name legal_name status created_at updated_at version ``` Do not permanently squeeze all contacts into one `email`, one `phone`, and one `contact_name`. Engineering customers commonly have multiple: ```text technical contacts billing contacts executive contacts site contacts contract contacts ``` Use: ```text engineering_client_contacts ``` Suggested contact fields: ```text id organization_id client_id name title department email phone contact_type is_primary created_at updated_at ``` Client REST: ```http GET /api/v1/engineering/clients POST /api/v1/engineering/clients GET /api/v1/engineering/clients/{clientId} PATCH /api/v1/engineering/clients/{clientId} POST /api/v1/engineering/clients/{clientId}/archive POST /api/v1/engineering/clients/{clientId}/restore GET /api/v1/engineering/clients/{clientId}/projects GET /api/v1/engineering/clients/{clientId}/invoices ``` Contact REST: ```http GET /api/v1/engineering/clients/{clientId}/contacts POST /api/v1/engineering/clients/{clientId}/contacts PATCH /api/v1/engineering/clients/{clientId}/contacts/{contactId} DELETE /api/v1/engineering/clients/{clientId}/contacts/{contactId} ``` Delete may be implemented as archival when contact history matters. Client restore is allowed only when organization policy and retention rules permit it. ## 28. Engineering Projects Suggested fields: ```text id organization_id client_id project_number name description discipline stage status project_manager_user_id start_date expected_completion_date completed_date budget_minor currency_code created_at updated_at version ``` REST: ```http GET /api/v1/engineering/projects POST /api/v1/engineering/projects GET /api/v1/engineering/projects/{projectId} PATCH /api/v1/engineering/projects/{projectId} POST /api/v1/engineering/projects/{projectId}/activate POST /api/v1/engineering/projects/{projectId}/close POST /api/v1/engineering/projects/{projectId}/archive ``` Purpose-built read models may be added when the frontend requires them: ```http GET /api/v1/engineering/projects/{projectId}/summary GET /api/v1/engineering/projects/{projectId}/timeline GET /api/v1/engineering/projects/{projectId}/budget ``` These are read-model endpoints, not necessarily separate aggregate tables. Do not put arbitrary budget-breakdown JSON into the core project row merely because the response can display it. Model detailed budget data in dedicated tables when that feature is implemented. ## 29. Engineering Project Members Suggested fields: ```text id organization_id project_id user_id project_role joined_at left_at ``` REST: ```http GET /api/v1/engineering/projects/{projectId}/members POST /api/v1/engineering/projects/{projectId}/members PATCH /api/v1/engineering/projects/{projectId}/members/{memberId} DELETE /api/v1/engineering/projects/{projectId}/members/{memberId} ``` --- ## 30. Engineering Project Phases Suggested fields: ```text id organization_id project_id name sequence status start_date end_date created_at updated_at ``` Typical phases: ```text Concept Preliminary Design Detailed Design Construction Inspection Closeout ``` REST: ```http GET /api/v1/engineering/projects/{projectId}/phases POST /api/v1/engineering/projects/{projectId}/phases PATCH /api/v1/engineering/projects/{projectId}/phases/{phaseId} POST /api/v1/engineering/projects/{projectId}/phases/{phaseId}/complete ``` --- ## 31. Engineering Sites Suggested fields: ```text id organization_id project_id name address latitude longitude created_at updated_at ``` REST: ```http POST /api/v1/engineering/projects/{projectId}/sites GET /api/v1/engineering/projects/{projectId}/sites GET /api/v1/engineering/sites/{siteId} PATCH /api/v1/engineering/sites/{siteId} ``` --- ## 32. Engineering Tasks Suggested fields: ```text id organization_id project_id title description status priority created_by_user_id assigned_to_user_id due_at completed_at created_at updated_at version ``` REST: ```http POST /api/v1/engineering/tasks GET /api/v1/engineering/tasks GET /api/v1/engineering/tasks/{taskId} PATCH /api/v1/engineering/tasks/{taskId} POST /api/v1/engineering/tasks/{taskId}/complete POST /api/v1/engineering/tasks/{taskId}/reopen POST /api/v1/engineering/tasks/{taskId}/cancel ``` --- ## 33. Engineering Designs Suggested fields: ```text id organization_id project_id design_number title description discipline status owner_user_id prepared_by_user_id approved_by_user_id approved_at created_at updated_at version ``` Suggested states: ```text draft under_review changes_requested approved rejected cancelled withdrawn superseded ``` REST: ```http GET /api/v1/engineering/projects/{projectId}/designs POST /api/v1/engineering/projects/{projectId}/designs GET /api/v1/engineering/designs/{designId} PATCH /api/v1/engineering/designs/{designId} POST /api/v1/engineering/designs/{designId}/submit-review POST /api/v1/engineering/designs/{designId}/request-changes POST /api/v1/engineering/designs/{designId}/approve POST /api/v1/engineering/designs/{designId}/reject POST /api/v1/engineering/designs/{designId}/cancel POST /api/v1/engineering/designs/{designId}/withdraw POST /api/v1/engineering/designs/{designId}/supersede POST /api/v1/engineering/designs/{designId}/assign POST /api/v1/engineering/designs/{designId}/unassign GET /api/v1/engineering/designs/{designId}/versions POST /api/v1/engineering/designs/{designId}/versions GET /api/v1/engineering/designs/{designId}/reviews POST /api/v1/engineering/designs/{designId}/reviews ``` ### Assignment Model Use: ```text engineering_design_assignments ``` Possible assignment roles: ```text owner designer reviewer approver checker ``` Suggested fields: ```text id organization_id design_id user_id assignment_role notes assigned_by_user_id assigned_at unassigned_at ``` Assignment does not automatically grant platform permission. Both RBAC and resource policy still apply. ### Design State Machine ```text draft ├── submit-review ───────────────► under_review └── cancel ──────────────────────► cancelled under_review ├── request-changes ─────────────► changes_requested ├── approve ─────────────────────► approved ├── reject ──────────────────────► rejected └── withdraw ────────────────────► withdrawn changes_requested ├── submit-review ───────────────► under_review └── withdraw ────────────────────► withdrawn rejected └── revise ──────────────────────► draft approved └── supersede ───────────────────► superseded ``` Use `cancelled` for work stopped before formal review. Use `withdrawn` for work intentionally removed after review workflow has started. Approval requires: ```text permission + project access + appropriate assignment/policy + valid professional qualification + valid design state + organization approval policy ``` Approval, rejection, withdrawal, and supersession are audited. Approval is idempotent. Do not approve by PATCHing `status`. ## 34. Design Versions and Reviews `engineering_design_versions`: ```text id design_id version_number document_id created_by_user_id created_at ``` `engineering_design_reviews`: ```text id organization_id design_id reviewer_user_id status comments reviewed_at ``` Possible review statuses: ```text pending approved changes_requested rejected ``` --- ## 35. Engineering Inspections Suggested fields: ```text id organization_id project_id site_id inspection_type inspector_user_id status outcome scheduled_at started_at performed_at cancelled_at summary created_at updated_at version ``` Lifecycle status: ```text draft scheduled in_progress completed cancelled ``` Outcome is separate: ```text passed passed_with_observations followup_required failed ``` This distinction matters. An inspection can be fully completed and still require corrective work. REST: ```http GET /api/v1/engineering/projects/{projectId}/inspections POST /api/v1/engineering/projects/{projectId}/inspections GET /api/v1/engineering/inspections/{inspectionId} PATCH /api/v1/engineering/inspections/{inspectionId} POST /api/v1/engineering/inspections/{inspectionId}/schedule POST /api/v1/engineering/inspections/{inspectionId}/start POST /api/v1/engineering/inspections/{inspectionId}/complete POST /api/v1/engineering/inspections/{inspectionId}/cancel GET /api/v1/engineering/inspections/{inspectionId}/findings POST /api/v1/engineering/inspections/{inspectionId}/findings POST /api/v1/engineering/inspections/{inspectionId}/followups GET /api/v1/engineering/inspections/{inspectionId}/followups ``` A follow-up may be: ```text corrective task new inspection or both ``` Do not encode all follow-up workflow into the original inspection's lifecycle state. Inspection completion: 1. validate inspector and project access 2. validate required fields 3. validate findings 4. calculate or confirm outcome 5. complete inspection 6. create corrective work/follow-up records when required 7. audit 8. write outbox event 9. notify appropriate participants Completion is idempotent. ## 36. Inspection Findings Suggested fields: ```text id inspection_id severity description status resolved_at ``` Possible severities: ```text observation minor major critical ``` REST: ```http POST /api/v1/engineering/inspections/{inspectionId}/findings PATCH /api/v1/engineering/inspection-findings/{findingId} POST /api/v1/engineering/inspection-findings/{findingId}/resolve ``` --- ## 37. Engineering Specifications Suggested fields: ```text id organization_id project_id specification_number title version status document_id created_at updated_at ``` --- ## 38. Engineering Change Requests Suggested fields: ```text id organization_id project_id request_number title description status requested_by_user_id approved_by_user_id estimated_cost_minor created_at updated_at ``` --- ## 38A. Engineering Project Budgets A single `budget_minor` column is sufficient only for a very early project total. When budget management enters scope, introduce: ```text engineering_project_budgets engineering_project_budget_items engineering_project_commitments engineering_project_cost_entries ``` ### Budget Suggested fields: ```text id organization_id project_id name currency_code status approved_by_user_id approved_at created_at updated_at version ``` ### Budget Item Suggested fields: ```text id organization_id budget_id category description allocated_amount_minor created_at updated_at ``` Do not casually store mutable: ```text spent_amount_minor committed_amount_minor ``` as independent sources of truth if those values are derived from time entries, expenses, purchase commitments, or invoices. Prefer: ```text authoritative cost/commitment records ↓ derived budget projections ``` If denormalized totals are needed for performance, update them transactionally and reconcile them. Potential REST: ```http GET /api/v1/engineering/projects/{projectId}/budgets POST /api/v1/engineering/projects/{projectId}/budgets GET /api/v1/engineering/budgets/{budgetId} PATCH /api/v1/engineering/budgets/{budgetId} POST /api/v1/engineering/budgets/{budgetId}/approve GET /api/v1/engineering/budgets/{budgetId}/items POST /api/v1/engineering/budgets/{budgetId}/items ``` Budget approval is an explicit command. --- ## 39. Engineering Time Entries Suggested fields: ```text id organization_id project_id user_id work_date duration_minutes description billable billing_rate_minor currency_code created_at updated_at ``` REST: ```http POST /api/v1/engineering/time-entries GET /api/v1/engineering/time-entries GET /api/v1/engineering/time-entries/{id} PATCH /api/v1/engineering/time-entries/{id} ``` Store duration as integer minutes. --- # Legal Domain ## 40. Legal Tables Initial tables: ```text legal_clients legal_matters legal_matter_members legal_cases legal_case_parties legal_courts legal_hearings legal_deadlines legal_documents legal_time_entries legal_retainers legal_conflict_checks legal_conflict_parties legal_conflict_matches ``` REST namespace: ```text /api/v1/legal ``` Core examples: ```http GET /api/v1/legal/matters POST /api/v1/legal/matters GET /api/v1/legal/matters/{matterId} PATCH /api/v1/legal/matters/{matterId} POST /api/v1/legal/matters/{matterId}/close POST /api/v1/legal/matters/{matterId}/reopen GET /api/v1/legal/matters/{matterId}/cases GET /api/v1/legal/matters/{matterId}/documents GET /api/v1/legal/matters/{matterId}/time-entries GET /api/v1/legal/matters/{matterId}/invoices POST /api/v1/legal/conflict-checks GET /api/v1/legal/conflict-checks/{conflictCheckId} POST /api/v1/legal/conflict-checks/{conflictCheckId}/approve POST /api/v1/legal/conflict-checks/{conflictCheckId}/decline ``` Legal remains a later vertical. These endpoints define intended boundaries, not a P0 build commitment. ## 41. Legal Matters Suggested fields: ```text id organization_id client_id matter_number title practice_area responsible_lawyer_user_id status opened_date closed_date created_at updated_at ``` --- ## 42. Legal Cases Suggested fields: ```text id organization_id matter_id case_number court_id jurisdiction case_type status filed_date created_at updated_at ``` --- ## 43. Legal Hearings Suggested fields: ```text id organization_id case_id hearing_type scheduled_at courtroom judge status notes ``` --- ## 44. Legal Conflict Checks Suggested tables: ```text legal_conflict_checks legal_conflict_parties legal_conflict_matches ``` Conflict-check fields: ```text id organization_id potential_client_name matter_description requested_by_user_id reviewed_by_user_id status decision decision_reason created_at reviewed_at version ``` Request example: ```json { "potentialClientName": "Acme Corporation", "relatedParties": [ { "name": "John Smith", "relationship": "CEO" }, { "name": "Acme Subsidiary LLC", "relationship": "Subsidiary" } ], "matterDescription": "Corporate acquisition" } ``` Response may contain possible matches: ```json { "data": { "id": "conflict_123", "status": "pending_review", "potentialConflicts": [ { "type": "possible_direct_adversity", "partyName": "Acme Corporation", "existingMatterId": "matter_456", "existingMatterNumber": "MAT-2026-089" } ] } } ``` The system should distinguish: ```text automated possible match ``` from: ```text lawyer-approved conflict determination ``` The software may assist discovery; it should not silently make the professional judgment. Approvals and declines are auditable commands. ## 45. Healthcare Tables Initial tables: ```text healthcare_patients healthcare_patient_contacts healthcare_patient_addresses healthcare_practitioners healthcare_appointments healthcare_encounters healthcare_clinical_records healthcare_clinical_record_versions healthcare_clinical_record_amendments healthcare_diagnoses healthcare_prescriptions healthcare_insurance_policies healthcare_allergies healthcare_medications ``` REST namespace: ```text /api/v1/healthcare ``` Examples: ```http GET /api/v1/healthcare/patients POST /api/v1/healthcare/patients GET /api/v1/healthcare/patients/{patientId} PATCH /api/v1/healthcare/patients/{patientId} POST /api/v1/healthcare/patients/{patientId}/archive GET /api/v1/healthcare/patients/{patientId}/appointments GET /api/v1/healthcare/patients/{patientId}/encounters GET /api/v1/healthcare/patients/{patientId}/clinical-records GET /api/v1/healthcare/patients/{patientId}/prescriptions GET /api/v1/healthcare/patients/{patientId}/allergies POST /api/v1/healthcare/encounters POST /api/v1/healthcare/encounters/{encounterId}/clinical-records GET /api/v1/healthcare/clinical-records/{recordId} GET /api/v1/healthcare/clinical-records/{recordId}/history GET /api/v1/healthcare/clinical-records/{recordId}/access-log POST /api/v1/healthcare/clinical-records/{recordId}/sign POST /api/v1/healthcare/clinical-records/{recordId}/amend ``` Healthcare is intentionally not treated as ordinary CRM plus extra columns. ## 46. Healthcare Patients Core patient fields: ```text id organization_id patient_number first_name middle_name last_name date_of_birth sex_or_administrative_gender_as_required status created_at updated_at version ``` Do not make a single default patient DTO return every available PHI field. Use minimum-necessary response shapes. Example general patient response: ```json { "data": { "id": "patient_123", "patientNumber": "PAT-2026-001", "name": { "firstName": "Alice", "middleName": "Marie", "lastName": "Johnson" }, "dateOfBirth": "1985-03-15", "status": "active", "version": 2 } } ``` More sensitive subresources should have separate permissions and endpoints where useful: ```text contact information addresses emergency contacts insurance policies clinical records prescriptions ``` Do not return insurance member IDs or emergency contact details on every patient read merely because the database has them. ## 47. Healthcare Practitioners Suggested fields: ```text id organization_id user_id specialty license_number license_jurisdiction credential_status created_at updated_at ``` --- ## 48. Healthcare Appointments Suggested fields: ```text id organization_id patient_id practitioner_id appointment_type starts_at ends_at status reason created_at updated_at ``` --- ## 49. Healthcare Encounters Suggested fields: ```text id organization_id patient_id practitioner_id appointment_id encounter_type started_at ended_at status ``` --- ## 50. Clinical Records Suggested tables: ```text healthcare_clinical_records healthcare_clinical_record_versions healthcare_clinical_record_amendments ``` Core record fields: ```text id organization_id patient_id encounter_id author_practitioner_id record_type sensitivity_level status signed_by_practitioner_id signed_at created_at updated_at version ``` Draft content may be editable according to workflow. Once signed/finalized: - do not overwrite history - create amendments or new versions - preserve previous signed content - audit reads when policy requires - audit all writes/signatures/amendments REST: ```http POST /api/v1/healthcare/encounters/{encounterId}/clinical-records GET /api/v1/healthcare/clinical-records/{recordId} PATCH /api/v1/healthcare/clinical-records/{recordId} # Only when editable/draft according to policy. POST /api/v1/healthcare/clinical-records/{recordId}/sign POST /api/v1/healthcare/clinical-records/{recordId}/amend GET /api/v1/healthcare/clinical-records/{recordId}/history GET /api/v1/healthcare/clinical-records/{recordId}/access-log ``` Clinical content representation should be designed around actual healthcare requirements and interoperability needs rather than permanently committing to one ad-hoc JSON SOAP-note structure. Sensitive record access should support an `accessReason` when organization or regulatory policy requires it. ## 51. Documents Use shared object storage. Database: ```text documents document_versions document_categories retention_policies ``` Binary data: ```text S3-compatible object storage ``` ### Document Suggested fields: ```text id organization_id name category_id classification retention_policy_id current_version_id created_by_user_id created_at updated_at ``` Classification examples: ```text public internal confidential restricted regulated ``` Avoid a single `is_confidential` boolean as the long-term security model. ### Document Version Suggested fields: ```text id organization_id document_id version_number storage_key mime_type size_bytes content_hash hash_algorithm uploaded_by_user_id created_at ``` The authoritative checksum belongs on the version because each binary revision has different content. Optional document-level metadata may include: ```text current_version_id current_version_number ``` but should not replace version-level integrity data. ### Metadata Use JSONB only for genuinely extensible metadata that does not deserve stable relational columns. Examples: ```text CAD-specific extraction results scanner metadata non-authoritative document properties ``` Do not place access control, retention state, ownership, or lifecycle rules inside arbitrary metadata JSON. ### Document Categories Suggested fields: ```text id organization_id profession nullable name parent_category_id created_at ``` If `profession` is nullable and shared categories must remain unique, PostgreSQL uniqueness must explicitly handle nulls. Options include: ```text UNIQUE NULLS NOT DISTINCT ``` where supported, or separate partial unique indexes for: ```text profession IS NULL profession IS NOT NULL ``` Do not rely on a plain nullable composite unique constraint and assume NULL behaves like a normal value. ### Upload Security Validate: ```text declared MIME extension magic bytes/content signature file size malware scan organization quota classification policy ``` A renamed executable is not a PDF merely because the filename developed ambition. ## 52. Document Upload Flow ```text Frontend ↓ Request upload authorization ↓ Backend validates tenant + permission + upload policy ↓ Create pending document/version ↓ Return signed upload URL ↓ Frontend uploads directly to object storage ↓ Backend finalizes upload ↓ Verify size / checksum / content type ↓ Malware and security scanning ↓ Apply classification / retention ↓ Mark version available ``` REST: ```http POST /api/v1/documents/upload-url POST /api/v1/documents/{documentId}/complete-upload GET /api/v1/documents/{documentId} GET /api/v1/documents/{documentId}/download-url POST /api/v1/documents/{documentId}/versions ``` ### File Policy Do not freeze arbitrary product quotas into architecture. Model a policy: ```text document_upload_policy ├── max_file_size_bytes ├── allowed_file_classes ├── organization_storage_quota_bytes ├── profession_overrides └── plan/tier overrides ``` Engineering may eventually allow file classes such as: ```text PDF images DWG/DXF spreadsheets office documents ``` Healthcare may later have different file policies. Exact limits are product/configuration decisions validated against storage cost, threat model, and customer needs. ## 53. Profession-Specific Document Links Use explicit relationship tables. Engineering: ```text engineering_project_documents engineering_design_documents engineering_inspection_documents ``` Legal: ```text legal_matter_documents legal_case_documents ``` Healthcare: ```text healthcare_patient_documents healthcare_encounter_documents ``` ### Engineering Project Documents Suggested link fields: ```text id organization_id project_id document_id category classification_override nullable linked_by_user_id linked_at unlinked_at ``` REST: ```http GET /api/v1/engineering/projects/{projectId}/documents POST /api/v1/engineering/projects/{projectId}/documents DELETE /api/v1/engineering/project-documents/{documentLinkId} ``` Link deletion may preserve historical linkage through `unlinked_at` when required. Example response: ```json { "data": [ { "documentLinkId": "projdoc_123", "category": "calculations", "document": { "id": "doc_456", "name": "structural_calculations.pdf", "classification": "confidential", "currentVersion": 2, "mimeType": "application/pdf", "sizeBytes": 2457600 } } ] } ``` Explicit link resources give stronger referential integrity than generic polymorphic foreign keys. ## 54. Billing Shared financial core: ```text invoices invoice_items payments ``` Profession-specific modules may extend billing workflows. Engineering examples: ```text project billing hourly billing milestone billing ``` Legal examples: ```text matter billing time billing retainers trust accounting ``` Healthcare examples: ```text insurance claims patient billing ``` REST: ```http POST /api/v1/invoices GET /api/v1/invoices GET /api/v1/invoices/{invoiceId} PATCH /api/v1/invoices/{invoiceId} POST /api/v1/invoices/{invoiceId}/issue POST /api/v1/invoices/{invoiceId}/void POST /api/v1/invoices/{invoiceId}/payments POST /api/v1/payments/{paymentId}/refund ``` --- ## 55. Money Representation Use integer minor units: ```json { "amountMinor": 12550, "currency": "USD" } ``` Meaning: ```text $125.50 ``` Never use floating point for money. --- ## 56. Audit Logging Table: ```text audit_events ``` Suggested fields: ```text id organization_id actor_type actor_user_id actor_service_account_id action resource_type resource_id request_id correlation_id ip_address user_agent metadata occurred_at ``` Audit events are append-only. ### Mandatory Engineering Audit Events ```text engineering.projects.create engineering.projects.close engineering.designs.approve engineering.designs.reject engineering.designs.supersede engineering.inspections.complete ``` ### Mandatory Legal Audit Events ```text legal.matters.create legal.matters.close legal.matters.reopen legal.conflicts.approve legal.conflicts.decline legal.retainers.manage ``` ### Mandatory Healthcare Audit Events ```text healthcare.records.read healthcare.records.write healthcare.records.sign healthcare.records.amend healthcare.prescriptions.write healthcare.prescriptions.sign ``` Example: ```json { "id": "audit_123", "organizationId": "org_456", "actorUserId": "user_789", "action": "healthcare.records.read", "resourceType": "healthcare_clinical_record", "resourceId": "record_456", "requestId": "req_abc", "ipAddress": "192.0.2.10", "userAgent": "Mozilla/5.0", "metadata": { "patientId": "patient_123", "recordType": "progress_note", "accessReason": "clinical_review" }, "occurredAt": "2026-08-26T01:30:00Z" } ``` Audit metadata must never contain: - passwords - access or refresh tokens - full clinical note content - secret keys - unnecessary payment data REST: ```http GET /api/v1/audit-events ``` No public create/update/delete endpoints. ## 57. Domain Events and Transactional Outbox Profession modules produce internal domain events. Examples: ```text engineering.project.created engineering.design.approved engineering.inspection.completed legal.matter.closed legal.conflict_check.approved healthcare.appointment.created healthcare.clinical_record.signed invoice.issued payment.recorded ``` Consumers: ```text notifications webhooks analytics search indexing integrations background workflows ``` Use: ```text outbox_events ``` Suggested fields: ```text id organization_id event_type aggregate_type aggregate_id payload occurred_at available_at processed_at attempt_count last_error dead_lettered_at ``` Transaction: ```text BEGIN business change audit event outbox event COMMIT ``` The outbox is **at-least-once delivery**, not magically exactly-once. Worker claim example: ```sql SELECT id FROM outbox_events WHERE processed_at IS NULL AND dead_lettered_at IS NULL AND available_at <= now() ORDER BY occurred_at FOR UPDATE SKIP LOCKED LIMIT 100; ``` Worker responsibilities: 1. claim committed event 2. process consumer action 3. mark processed on success 4. increment attempts on failure 5. schedule retry with backoff 6. dead-letter after policy threshold 7. emit metrics 8. preserve replay/debug metadata ### Critical Failure Case A worker may: ```text perform external side effect ↓ crash ↓ fail to mark event processed ↓ event is retried ``` Therefore every external consumer must support idempotency. Examples: ```text payment provider command → provider idempotency key webhook delivery → delivery/event ID email notification → dedupe key if duplicate mail is unacceptable search indexing → upsert by entity/version ``` `FOR UPDATE SKIP LOCKED` prevents concurrent claims. It does **not** prevent duplicate side effects after a crash. Workers may be awakened by queue notifications, but must still poll durable outbox state so lost wake-ups do not strand events. ## 57A. Webhooks and External Integrations Webhooks are a shared platform capability, not profession-specific transport code. Configuration REST: ```http GET /api/v1/webhooks POST /api/v1/webhooks GET /api/v1/webhooks/{webhookId} PATCH /api/v1/webhooks/{webhookId} DELETE /api/v1/webhooks/{webhookId} POST /api/v1/webhooks/{webhookId}/test POST /api/v1/webhooks/{webhookId}/rotate-secret ``` Delivery REST: ```http GET /api/v1/webhook-deliveries GET /api/v1/webhook-deliveries/{deliveryId} POST /api/v1/webhook-deliveries/{deliveryId}/retry ``` Suggested tables: ```text webhooks webhook_event_subscriptions webhook_deliveries ``` Webhook fields: ```text id organization_id url status secret_ciphertext or signing_key_reference created_by_user_id created_at updated_at ``` Do not return a secret hash to the client. ### Secret Handling If using symmetric HMAC signing: ```text generate secret ↓ show plaintext once ↓ encrypt using KMS/key-management system ↓ store ciphertext ↓ decrypt only for signing ``` A one-way hash alone is insufficient because the server must possess the signing material. Alternative: ```text asymmetric signing + published verification key ``` ### Delivery Model Each delivery records: ```text id organization_id webhook_id event_id attempt_number request_timestamp response_status response_summary delivered_at failed_at next_attempt_at ``` Webhook workers require: ```text timeouts retry with backoff dead-letter/failure state request signing event IDs idempotency guidance for consumers delivery history manual replay ``` Events should include stable identifiers so consumers can deduplicate. Example: ```json { "id": "evt_123", "type": "engineering.design.approved", "organizationId": "org_456", "occurredAt": "2026-08-26T12:00:00Z", "data": { "designId": "design_789" } } ``` --- ## 58. Background Jobs Workers handle: ```text Email SMS Notifications PDF/report generation File security scanning Document processing Imports Exports Bulk updates Webhook delivery Search indexing Large data operations ``` Architecture: ```text API ↓ Queue ↓ Worker ``` ### Async Job Resource Use a shared job model for long-running user-requested operations. Suggested table: ```text jobs ``` Fields: ```text id organization_id requested_by_user_id job_type status input_reference result_reference progress_percent created_at started_at completed_at failed_at error_code error_summary ``` States: ```text queued running completed failed cancelled ``` REST: ```http GET /api/v1/jobs/{jobId} GET /api/v1/jobs/{jobId}/result POST /api/v1/jobs/{jobId}/cancel ``` ### Import / Export Do not create asynchronous side effects with `GET`. Engineering examples: ```http POST /api/v1/engineering/project-imports POST /api/v1/engineering/project-exports POST /api/v1/engineering/time-entry-imports POST /api/v1/engineering/time-entry-exports ``` Response: ```http 202 Accepted ``` ```json { "data": { "jobId": "job_123", "status": "queued" } } ``` Initial formats may include: ```text CSV JSON ``` Import requirements: ```text validation report row-level errors all-or-partial mode explicitly defined idempotency strategy audit event job result artifact ``` Export requirements: ```text authorization applied before generation signed result URL expiration audit where data sensitivity requires it ``` ## 59. Redis Use Redis as an acceleration and coordination layer, not the authoritative system of record. Appropriate uses: ```text job queue rate-limit counters short-lived authorization caches organization configuration cache session lookup acceleration idempotency lookup acceleration distributed locks when justified ``` ### Cache Layers L1 optional application-memory cache: ```text static permission definitions non-sensitive configuration ``` L2 Redis shared cache: ```text organization settings membership snapshots role permission snapshots rate-limit counters session lookup cache recent idempotency lookups ``` CDN: ```text frontend static assets explicitly public assets only ``` Do not cache private professional API responses at a CDN by default. ### Cache Invalidation Invalidate or version caches when: ```text membership changes role permissions change organization settings change professional credentials change session is revoked profession module enablement changes ``` High-risk authorization decisions must not depend solely on stale cached credential state. ### Idempotency Durability Redis may improve idempotency lookup latency, but PostgreSQL remains authoritative for high-risk commands. ## 60. Pagination Use cursor pagination. Example: ```http GET /api/v1/engineering/projects?limit=25 ``` Response: ```json { "data": [], "meta": { "pagination": { "nextCursor": "...", "hasMore": true } } } ``` Maximum page size: ```text 100 ``` --- ## 61. Filtering Use explicit resource-specific filters. Examples: ```http GET /api/v1/engineering/projects?status=active&discipline=structural GET /api/v1/engineering/tasks?status=todo&assignedToUserId=user_123 ``` Do not build a generic query DSL in v1. --- ## 62. Sorting Examples: ```http GET /api/v1/engineering/projects?sort=createdAt GET /api/v1/engineering/projects?sort=-createdAt ``` Only explicitly supported fields may be sorted. --- ## 63. Search Start with PostgreSQL search. Engineering search may cover: ```text project number project name client name ``` Legal: ```text matter number client case number ``` Healthcare: ```text patient number patient identity ``` Healthcare search requires stricter privacy and authorization controls. Potential PostgreSQL capabilities: - B-tree indexes for exact/filter queries - PostgreSQL full-text search where appropriate - `pg_trgm` only when fuzzy search requirements justify it Do not introduce Elasticsearch/OpenSearch until real query volume, relevance requirements, or indexing features justify another distributed system. Do not create every conceivable search index on day one. Indexes cost memory, storage, and write performance. ## 64. Optimistic Concurrency Important mutable resources should use a version field. Example: ```json { "id": "project_123", "version": 6 } ``` Update: ```json { "version": 6, "name": "Central Tower Phase II" } ``` If the current database version differs: ```text 409 CONCURRENT_MODIFICATION ``` --- ## 65. Domain-Oriented REST Important state transitions use explicit command endpoints. Good: ```http POST /engineering/projects/{id}/close POST /engineering/designs/{id}/approve POST /engineering/tasks/{id}/complete POST /engineering/inspections/{id}/complete POST /invoices/{id}/issue ``` Avoid: ```http PATCH /resource/{id} { "status": "approved" } ``` when the change has significant rules or side effects. --- ## 66. Transaction Boundaries Create project: ```text BEGIN create project assign project manager write audit event write outbox event COMMIT ``` Approve design: ```text BEGIN validate permission validate project access validate credentials validate design state create review result mark approved write audit event write outbox event COMMIT ``` --- ## 67. Request Context Every authenticated request should resolve: ```text RequestContext { requestId userId sessionId organizationId membershipId permissions } ``` Profession modules consume this context. --- ## 68. Request IDs Every request has: ```http X-Request-Id ``` If missing, the server generates one. Use it in: - logs - audit context - error diagnostics - asynchronous correlation --- ## 69. OpenAPI Maintain: ```text openapi.yaml ``` Use OpenAPI 3.1. Production server example: ```yaml servers: - url: https://api.example.com/api/v1 ``` The server URL and path definitions must remain consistent with the platform base path. OpenAPI defines: - routes - request DTOs - response DTOs - security schemes - organization header - request IDs - idempotency header - pagination - filters - error schemas - examples - profession tags Security scheme: ```yaml components: securitySchemes: bearerAuth: type: http scheme: bearer bearerFormat: JWT ``` Reusable headers/parameters: ```text X-Organization-Id X-Request-Id Idempotency-Key limit cursor ``` CI must validate the OpenAPI document. Contract tests should detect drift between implementation and specification. Generated clients may be used by the separate frontends, but generated transport code should not dictate frontend domain architecture. ## 70. DTO Rule Database models are not public API contracts. Use: ```text Request DTO Response DTO ``` A database migration should not accidentally change the public API. --- ## 71. Backend Module Structure Recommended: ```text src/ ├── core/ │ ├── auth/ │ ├── organizations/ │ ├── memberships/ │ ├── authorization/ │ ├── documents/ │ ├── billing/ │ ├── audit/ │ └── events/ │ ├── engineering/ │ ├── clients/ │ ├── projects/ │ ├── project-members/ │ ├── phases/ │ ├── sites/ │ ├── tasks/ │ ├── designs/ │ ├── inspections/ │ └── specifications/ │ ├── legal/ │ ├── clients/ │ ├── matters/ │ ├── cases/ │ ├── hearings/ │ ├── conflicts/ │ └── retainers/ │ └── healthcare/ ├── patients/ ├── practitioners/ ├── appointments/ ├── encounters/ ├── records/ └── prescriptions/ ``` --- ## 72. Internal Module Structure Example: ```text projects/ ├── domain/ │ ├── project.entity.ts │ ├── project-status.ts │ └── project.errors.ts │ ├── application/ │ ├── commands/ │ │ ├── create-project.ts │ │ ├── update-project.ts │ │ └── close-project.ts │ │ │ └── queries/ │ ├── get-project.ts │ └── list-projects.ts │ ├── infrastructure/ │ └── project.repository.ts │ └── api/ ├── project.controller.ts ├── project.request.ts └── project.response.ts ``` --- ## 73. Controllers Controllers should handle: ```text HTTP authentication context input DTO parsing application command/query invocation response mapping ``` Controllers should not contain: ```text business rules raw SQL role logic transaction orchestration email sending audit implementation ``` --- ## 74. Commands and Queries Mutations use commands. Examples: ```text CreateEngineeringProjectCommand ApproveEngineeringDesignCommand CloseLegalMatterCommand CompleteHealthcareEncounterCommand ``` Reads use queries. Examples: ```text GetEngineeringProjectQuery ListLegalMattersQuery GetHealthcarePatientQuery ``` --- ## 75. Repositories Use domain-specific repositories. Examples: ```text EngineeringProjectRepository LegalMatterRepository HealthcarePatientRepository ``` Avoid one massive generic repository abstraction that eventually needs dozens of flags. --- ## 76. Security Baseline Minimum controls: ```text TLS everywhere Argon2id or equivalent strong password hashing short-lived access tokens refresh-token rotation refresh-token reuse detection server-side session revocation rate limiting anti-automation controls RBAC resource policies credential-aware authorization tenant isolation input validation SQL injection protection signed object-storage URLs file-content validation malware scanning audit trails secret management encryption at rest dependency scanning container/image scanning security headers request/correlation IDs backup and restore testing ``` ### Rate Limiting Model policy rather than baking arbitrary numbers into architecture. Example: ```typescript interface RateLimitRule { routePattern: string; method: string; windowSeconds: number; maxRequests: number; scope: 'user' | 'organization' | 'ip' | 'email' | 'session'; } ``` Policy classes: ```text authentication password recovery general API search upload authorization report generation webhooks/integrations clinical record reads ``` Rate-limit values are configuration derived from: ```text security testing load testing observed traffic customer tier endpoint cost abuse risk ``` Do not grant normal tenant roles blanket rate-limit bypass. Administrative exceptions, if any, require explicit trusted-system policy. Return: ```http 429 Too Many Requests Retry-After: ... ``` Error: ```text RATE_LIMIT_EXCEEDED ``` ### Secrets Use managed secrets/key management where possible. Never put real secrets in source-controlled examples. Prefer JWT asymmetric signing or managed signing keys with rotation capability. ## 77. Data Classification Suggested classes: ### Public ```text marketing configuration ``` ### Internal ```text organization settings tasks ``` ### Confidential ```text engineering documents legal matters billing ``` ### Highly Sensitive ```text clinical records professional credentials authentication secrets ``` --- ## 78. Healthcare Security Before healthcare production use, define: ```text privacy model minimum-necessary access model clinical access policies break-glass/emergency access policy if required audit policy record-signing policy amendment policy retention policy credential policy scope-of-practice policy jurisdiction requirements encryption strategy consent requirements data residency requirements backup/restore handling export/portability requirements breach-response requirements ``` Healthcare is a stricter security tier. Key rules: 1. default patient responses do not contain all available PHI 2. clinical record reads may be auditable events 3. signed records are immutable except through explicit amendment/version workflows 4. prescribing authorization is jurisdiction-specific 5. privileged clinical commands revalidate professional authority 6. caches must not allow revoked credentials to remain effective for high-risk writes 7. healthcare search results themselves are protected data 8. access logs may require dedicated permissions 9. do not claim regulatory compliance from architecture alone ## 79. Observability Use: ```text structured logs metrics distributed tracing request IDs correlation IDs ``` Recommended: ```text OpenTelemetry ``` ### Core Metrics API: ```text api_requests_total api_errors_total api_request_duration_seconds ``` Authentication: ```text auth_login_attempts_total auth_token_refresh_total auth_refresh_reuse_detections_total auth_sessions_revoked_total ``` Authorization/security: ```text cross_tenant_access_attempts_total tenant_isolation_invariant_failures_total authorization_denials_total credential_policy_denials_total rate_limit_events_total ``` Important distinction: ```text cross_tenant_access_attempt = request attempted another tenant's resource ``` This may be a stale link, mistake, or attack. ```text tenant_isolation_invariant_failure = our system nearly or actually created/returned cross-tenant data ``` That is a high-severity internal correctness/security incident. Outbox/jobs/webhooks: ```text outbox_events_pending outbox_events_failed_total outbox_processing_duration_seconds jobs_queued jobs_failed_total job_duration_seconds webhook_delivery_attempts_total webhook_delivery_failures_total webhook_delivery_latency_seconds ``` Database: ```text db_pool_active db_pool_waiting db_query_duration_seconds db_transaction_duration_seconds ``` Business metrics may include: ```text engineering_projects_created_total engineering_designs_approved_total engineering_inspections_completed_total invoices_issued_total ``` Avoid patient-specific or sensitive identifiers in metric labels. ### Alerts Examples: ```text refresh token reuse detected tenant isolation invariant failure outbox backlog exceeds SLO webhook failure spike database pool saturation error-rate spike latency regression backup failure malware scanner unavailable ``` Thresholds are calibrated from real environments rather than copied from a review document. ### SLOs Define by endpoint class. Interactive CRUD, reports, file orchestration, and background jobs should not share one arbitrary latency target. ## 80. Logging Useful fields: ```text request_id route method status duration user_id when appropriate organization_id when appropriate ``` Never log: ```text passwords tokens clinical record text full sensitive documents payment secrets ``` --- ## 81. Testing Strategy ### Unit Tests Test: ```text domain rules state transitions authorization policies credential policies money calculations idempotency request hashing ``` ### Property-Based Tests Use property-based testing for high-value domain state machines. Candidates: ```text engineering design lifecycle engineering inspection lifecycle invoice lifecycle payment state transitions membership/role invariants ``` Correct properties: ```text every successful transition ends in a valid state every forbidden transition is rejected terminal states reject prohibited actions required invariants survive every valid transition transition sequences never bypass required approval/credential rules ``` Do not assert that every random state/action pair succeeds. Many are supposed to fail. ### Integration Tests Test: ```text repositories tenant-aware foreign keys PostgreSQL constraints transactions outbox persistence idempotency persistence cache invalidation job persistence webhook delivery persistence ``` ### API Tests Every important endpoint covers: ```text happy path request validation authentication organization context permission denial scope denial credential denial where relevant cross-tenant access concurrent modification invalid state transition idempotent replay idempotency conflict audit creation outbox creation ``` ### Outbox Reliability / Chaos Tests Test: ```text worker crash before side effect worker crash after side effect but before marking processed two workers competing for same row temporary dependency outage retry/backoff behavior dead-letter behavior consumer idempotency lost worker wake-up replay ``` The dangerous scenario is: ```text external side effect succeeds worker dies event retries ``` Tests must prove the consumer does not create an unacceptable duplicate. ### Tenant Security Tests Test both: ```text external cross-tenant access attempts ``` and: ```text internal cross-tenant data invariant failures ``` These are different classes of failure. ### Performance Tests Create realistic profiles: ```text interactive reads interactive writes search dashboard read models reporting file upload orchestration outbox processing webhook bursts notification bursts ``` Measure: ```text p50 p95 p99 throughput error rate database saturation queue backlog ``` Set production SLO gates only after a realistic baseline exists. ### Coverage Track code coverage. Do not treat a single percentage such as `90%` as proof of quality. Critical-path expectations are stronger: ```text all tenant-isolation paths tested all financial commands tested all regulated commands tested all state transitions tested all critical authorization policies tested ``` ## 82. Tenant Security Tests For every major resource, attempt: ```text Organization A resource using Organization B context ``` Test: ```text read update delete/action list filtering search documents ``` Expected result: ```text 404 / denied ``` --- ## 83. Engineering MVP Engineering is the first vertical. Initial features: ```text Authentication Organization management Users / memberships / roles Engineering clients Projects Project members Project phases Tasks Sites Documents Basic design records Inspections Time entries Basic billing Audit history ``` Do not initially build: ```text advanced CAD integration BIM integration full document markup advanced resource planning procurement complex accounting AI design analysis IoT integrations ``` --- ## 84. Engineering MVP Workflow ```text User registers ↓ Creates engineering organization ↓ Invites engineer ↓ Assigns role ↓ Creates client ↓ Creates project ↓ Assigns project team ↓ Creates project phases ↓ Creates tasks ↓ Uploads documents ↓ Creates design ↓ Reviews / approves design ↓ Schedules inspection ↓ Records inspection findings ↓ Records engineering time ↓ Creates invoice ↓ Records payment ↓ Closes project ↓ Audit history contains lifecycle ``` --- ## 85. Development Phases ### Phase 0: Architecture Foundation Deliver: ```text domain boundaries database conventions REST conventions authorization model organization-context rules session/token model idempotency strategy error taxonomy OpenAPI skeleton engineering state machines migration conventions threat model ``` ### Phase 1: Shared Platform Core Build: ```text auth sessions refresh-token families token rotation/revocation users organizations organization professions membership invitations memberships roles permissions authorization audit outbox request context idempotency persistence rate-limit framework observability foundation ``` ### Phase 2: Engineering CRM Build: ```text engineering_clients engineering_client_contacts client archive/restore ``` ### Phase 3: Engineering Projects Build: ```text engineering_projects engineering_project_members engineering_project_phases activation/close/archive ``` ### Phase 4: Work and Site Management Build: ```text engineering_tasks engineering_sites ``` ### Phase 5: Documents Build: ```text documents document_versions document categories classification retention policy references signed uploads content verification malware scanning engineering document links ``` ### Phase 6: Engineering Designs Build: ```text engineering_designs design assignments design versions design reviews state machine credential-aware approval audit outbox idempotency ``` ### Phase 7: Engineering Inspections Build: ```text inspection lifecycle inspection outcome findings corrective work follow-up inspections attachments audit outbox idempotency ``` ### Phase 8: Time, Budgets, and Billing Build: ```text engineering_time_entries project budgets when in product scope invoices invoice items payments financial idempotency reconciliation ``` ### Phase 9: Notifications, Jobs, and Webhooks Build: ```text in-app notifications email async jobs imports/exports webhook configuration webhook delivery/retry dead-letter handling ``` ### Phase 10: Reporting and Search Build: ```text project status overdue work inspection status billable time revenue outstanding invoices engineering dashboard read models ``` Add advanced indexes, materialized views, read replicas, or external search only if measured need justifies them. ### Phase 11: Legal Vertical Validate the shared core against: ```text matters cases conflicts legal deadlines retainers ethical-wall/restricted access requirements ``` ### Phase 12: Healthcare Readiness and Vertical Before implementation: ```text healthcare threat model privacy review jurisdiction analysis credential/scope-of-practice policy record signing/amendment model retention model audit requirements ``` Then implement healthcare. ### Estimation Rule These are dependency-ordered milestones, not calendar promises. Calendar estimates are produced only after: ```text team size frontend scope UX designs cloud choices third-party providers security requirements QA capacity engineering-domain details ``` are known. ## 86. Legal Expansion Only after engineering proves the shared platform assumptions. Build: ```text Legal Client ↓ Matter ↓ Case ↓ Hearings / Deadlines / Documents ``` Do not redesign engineering around legal terminology. Extract only genuinely reusable infrastructure. --- ## 87. Healthcare Expansion Healthcare comes after: - core platform is stable - audit model is proven - permission model is proven - tenant isolation is tested - retention and encryption strategies are defined Healthcare should be treated as its own security and compliance workstream. --- ## 88. Deployment Environments Use: ```text development testing staging production ``` Each environment has independent: ```text database object storage secrets queues API keys ``` --- ## 89. Initial Deployment Architecture ```text CDN │ ├── Engineering Web ├── Legal Web └── Healthcare Web Load Balancer │ Backend API │ ├── PostgreSQL ├── Redis ├── Object Storage └── Queue │ Workers ``` Prefer managed infrastructure where practical. --- ## 90. Backup Strategy Database: ```text automated backups point-in-time recovery tested restores ``` Object storage: ```text versioning retention policies backup or replication where required ``` A backup strategy is incomplete until restoration is tested. --- ## 91. Migration Strategy Use explicit immutable migration files. Recommended naming: ```text YYYYMMDDHHMMSS_description.sql ``` Example: ```text 20260826010000_create_organizations.sql 20260826011000_create_users.sql 20260826012000_create_memberships.sql 20260826013000_create_rbac.sql 20260826014000_create_audit_outbox.sql 20260826015000_create_engineering_clients.sql ``` ### UUID Standard The platform uses UUIDv7. Supported implementation choices: ```text PostgreSQL 18+: use native uuidv7() if database-generated identifiers are desired Earlier PostgreSQL: generate UUIDv7 in the application or use a controlled extension ``` Database columns remain PostgreSQL `UUID`. The rule is consistency, not ideological loyalty to one generation layer. Do not silently fall back to UUIDv4 while documenting UUIDv7. ### Production Migration Rules Use expand/contract: ```text 1. add backward-compatible schema 2. deploy code supporting old + new schema 3. backfill/migrate 4. switch reads/writes 5. observe 6. remove obsolete schema later ``` For destructive changes: ```text backup/restore plan compatibility window production-like dry run explicit approval post-migration verification ``` Do not assume a destructive database migration can always be reversed by a simple down migration. Never use automatic ORM schema synchronization in production. ## 92. Technology Recommendation Backend: ```text TypeScript NestJS or Fastify-based architecture ``` Database: ```text PostgreSQL ``` ORM/query layer candidates: ```text Prisma Drizzle Kysely ``` Frontend: ```text React / Next.js ``` Queue: ```text Redis + BullMQ ``` Storage: ```text S3-compatible storage ``` Observability: ```text OpenTelemetry ``` Containers: ```text Docker ``` --- ## 93. REST API Milestones ### Milestone 1: Platform Access and Security ```http POST /auth/register POST /auth/login POST /auth/token/refresh POST /auth/token/revoke POST /auth/token/revoke-all GET /auth/sessions DELETE /auth/sessions/{sessionId} GET /me POST /organizations GET /me/organizations POST /membership-invitations GET /memberships GET /roles POST /roles GET /permissions ``` Includes: ```text explicit organization context session revocation refresh-token reuse detection audit foundation outbox foundation idempotency foundation rate limiting ``` ### Milestone 2: Engineering Clients ```http GET /engineering/clients POST /engineering/clients GET /engineering/clients/{id} PATCH /engineering/clients/{id} POST /engineering/clients/{id}/archive POST /engineering/clients/{id}/restore GET /engineering/clients/{id}/projects ``` ### Milestone 3: Engineering Projects ```http GET /engineering/projects POST /engineering/projects GET /engineering/projects/{id} PATCH /engineering/projects/{id} POST /engineering/projects/{id}/activate POST /engineering/projects/{id}/close POST /engineering/projects/{id}/archive GET /engineering/projects/{id}/summary ``` Timeline and budget read models follow when the frontend requires them. ### Milestone 4: Collaboration ```http POST /engineering/projects/{id}/members GET /engineering/projects/{id}/members POST /engineering/tasks GET /engineering/tasks POST /engineering/tasks/{id}/complete ``` ### Milestone 5: Sites and Documents Build: ```text engineering sites signed file uploads document versions malware scanning project document links ``` ### Milestone 6: Designs Build: ```text design lifecycle versions reviews submit-review request-changes approve reject supersede credential validation audit + outbox + idempotency ``` ### Milestone 7: Inspections Build: ```text schedule start complete cancel findings finding resolution audit + outbox + idempotency ``` ### Milestone 8: Commercial Workflows Build: ```text time entries invoices payments refunds financial idempotency reports ``` ## 94. Architecture Rules to Freeze 1. REST is the primary frontend and integration API. 2. Base path is `/api/v1`. 3. OpenAPI 3.1 is the public API contract. 4. GraphQL is not part of v1. 5. Begin as one modular monolith backend. 6. Each profession has its own frontend. 7. Each profession owns its domain tables and state machines. 8. Shared modules provide infrastructure rather than forced domain abstractions. 9. Every tenant-owned row contains `organization_id`. 10. Tenant-scoped requests require explicit `X-Organization-Id`. 11. The API never silently selects an organization. 12. Tenant boundaries are enforced in queries and database constraints. 13. Cross-tenant resources appear nonexistent. 14. Authorization is server-side and deny-by-default. 15. Roles and professional qualifications are separate. 16. High-risk professional commands validate authoritative credential state. 17. Sessions and refresh-token records are separate concepts. 18. Refresh-token rotation uses token families and reuse detection. 19. UUIDv7 is the identifier standard. 20. PostgreSQL 18 native `uuidv7()` may be used when PostgreSQL 18+ is the baseline. 21. Important domain transitions use explicit REST command endpoints. 22. High-risk commands use durable idempotency. 23. Redis may accelerate idempotency but is not authoritative for financial/regulated commands. 24. Profession-specific state transitions are explicitly modeled and tested. 25. Engineering design cancellation and post-review withdrawal are semantically distinct where needed. 26. Inspection lifecycle and inspection outcome are separate dimensions. 27. Follow-up inspection work is linked work, not an overloaded lifecycle status. 28. Shared document binaries live in object storage. 29. Document checksum belongs to document versions. 30. Document classification is multi-level, not a single confidentiality boolean. 31. File upload policy is configurable by organization/profession/product tier. 32. Uploads validate declared MIME, extension, content signature, size, quota, and malware status. 33. Explicit document-link tables are preferred over generic polymorphic references. 34. Domain events use a transactional outbox. 35. Outbox semantics are at-least-once. 36. Every outbox consumer that can create external side effects is idempotent. 37. Webhooks are a shared platform service with delivery history and retry. 38. HMAC webhook signing material must be recoverable securely, normally encrypted with managed key protection. 39. Async imports, exports, and reports use job resources and return `202 Accepted`. 40. `GET` endpoints do not create export jobs. 41. Engineering clients support multiple contacts. 42. Engineering budget detail uses dedicated tables when budget management enters scope. 43. Derived spent/committed budget totals must not become uncontrolled duplicate financial truth. 44. PostgreSQL remains the authoritative transactional datastore. 45. Redis is an acceleration/coordination layer. 46. Search starts with PostgreSQL. 47. External search, read replicas, materialized views, and partitioning require measured evidence. 48. API collections use cursor pagination. 49. Important mutable resources use optimistic concurrency. 50. Database entities are not serialized directly as public API contracts. 51. Errors use stable codes. 52. Important and regulated actions are audited. 53. Sensitive healthcare reads are audited when policy requires. 54. Signed clinical records use sign/amend/version workflows. 55. Prescribing authority is jurisdiction and scope-of-practice policy, not a hard-coded profession. 56. Production migrations follow expand/contract. 57. Destructive schema changes are not assumed to be trivially reversible. 58. Secrets are managed outside source control. 59. Rate limits are configurable policies calibrated by security/load evidence. 60. CI validates types, tests, OpenAPI, migrations, and security scans. 61. Property-based tests are used for high-value state machines. 62. Outbox/job/webhook reliability is tested under failure and concurrency. 63. Internal tenant-isolation invariant failures and external cross-tenant attempts are separate observability signals. 64. Critical-path tests matter more than a vanity coverage percentage. 65. Engineering is the first implemented product vertical. 66. Legal follows after the engineering product validates shared assumptions. 67. Healthcare requires explicit security/privacy/jurisdiction readiness work before implementation. 68. Architecture documentation never equates "designed for" with "certified/compliant". 69. Milestones define implementation order; calendar estimates require actual delivery context. ## 95. Required Design Artifacts Maintain: ```text 01_PROJECT_ARCHITECTURE.md 02_DATABASE_CONVENTIONS.md 03_AUTHORIZATION_MODEL.md 04_AUTH_SESSION_MODEL.md 05_ENGINEERING_DOMAIN.md 06_ENGINEERING_DATABASE_SCHEMA.md 07_API_CONVENTIONS.md 08_ENGINEERING_API_SPEC.md 09_FRONTEND_ARCHITECTURE.md 10_DOCUMENT_SECURITY_MODEL.md 11_WEBHOOK_INTEGRATION_MODEL.md 12_ASYNC_JOB_MODEL.md 13_SECURITY_MODEL.md 14_DEPLOYMENT_ARCHITECTURE.md 15_OBSERVABILITY_MODEL.md 16_TESTING_STRATEGY.md 17_MVP_BACKLOG.md 18_OPENAPI.yaml ``` Supporting state-machine documents should exist for: ```text engineering projects engineering designs engineering inspections invoices/payments clinical records when healthcare begins ``` ## 96. Recommended Implementation Order ```text Foundation ↓ Authentication ↓ Organizations ↓ Memberships ↓ RBAC ↓ Engineering Clients ↓ Engineering Projects ↓ Project Team ↓ Tasks ↓ Sites ↓ Documents ↓ Designs ↓ Inspections ↓ Time Tracking ↓ Billing ↓ Notifications ↓ Reports ↓ Legal Vertical ↓ Healthcare Vertical ``` --- ## 97A. Database Indexing Strategy All tenant-owned tables need efficient tenant scoping. Baseline: ```text (organization_id, id) ``` Common list access often benefits from: ```text (organization_id, created_at) ``` Query-specific examples: ```text (organization_id, status) (organization_id, client_id) (organization_id, project_id) (organization_id, assigned_to_user_id) ``` ### Rules 1. every index corresponds to a known query, ordering, or constraint 2. column order follows real predicates 3. validate with `EXPLAIN (ANALYZE, BUFFERS)` 4. include production-like cardinality in testing 5. measure write amplification 6. do not index every field 7. introduce trigram/full-text indexes only for actual search requirements Potential later tools: ```text covering indexes materialized views read replicas table partitioning external search ``` These are evidence-driven scaling mechanisms, not baseline dependencies. ### Document Category Uniqueness If a nullable field such as profession participates in uniqueness: ```text organization_id profession nullable name ``` do not assume plain uniqueness treats NULL as one shared value. Use PostgreSQL-supported null-aware uniqueness or partial unique indexes according to the selected PostgreSQL version. --- ## 97B.## 97B. CI/CD and Deployment Gates Pipeline stages: ```text lint/typecheck ↓ unit tests ↓ integration tests ↓ OpenAPI validation + contract tests ↓ security/dependency scan ↓ container build + image scan ↓ migration compatibility check ↓ deploy development ↓ smoke tests ↓ deploy staging ↓ E2E + performance/security baseline ↓ manual production approval ↓ production deployment ↓ post-deploy verification ``` Production deployment should support: ```text rolling or blue/green application deployment backward-compatible database migrations health checks fast application rollback feature flags for incomplete features observability gates ``` Database schema rollback is not treated as equivalent to application rollback. ### Configuration and Secrets Non-secret configuration may use environment variables. Secrets should use a managed secret store where possible: ```text database credentials Redis credentials JWT/private signing keys object storage credentials SMTP/API provider credentials monitoring credentials ``` Do not publish real secrets in sample configuration. Organization profession enablement remains primarily data-driven through `organization_professions`. Global feature flags may be used for staged rollout, kill switches, or incomplete features. --- ## 97C. Review-Driven Deferred Decisions The following ideas are valid possibilities but are explicitly **not frozen into v1**: ```text read replicas materialized views Elasticsearch/OpenSearch universal 100 MB file limit fixed 100 req/min user limit fixed 1000 req/hour organization limit specific cache-hit-ratio target specific p95 latency promise database-per-tenant microservices GraphQL ``` These require evidence from: ```text load tests security analysis customer requirements compliance requirements real production workloads ``` This prevents benchmark-shaped guesses from becoming architecture law. --- ## 97D. Production Readiness Gates Architecture being coherent does not mean production is safe. Before production, require evidence in these categories. ### Security ```text TLS configured password hashing configured refresh rotation/reuse detection tested session revocation tested tenant isolation tests passing authorization/credential policies tested rate limiting active secrets managed outside source control file security scanning active security review completed ``` ### Reliability ```text database backups automated restore tested object storage recovery strategy tested outbox monitoring active job queue monitoring active webhook retry/dead-letter behavior tested health checks configured dependency failures tested ``` ### Data Integrity ```text tenant-aware foreign keys present where required financial invariants tested migration tested on production-like data idempotency tested for high-risk commands optimistic concurrency tested audit integrity tested ``` ### Contract / API ```text OpenAPI validates contract tests pass error schema consistent versioning rules documented client SDK generation validated if used ``` ### Performance ```text load test executed realistic SLOs defined database pool configured key queries analyzed outbox/job backlogs remain within SLO ``` ### Critical Domain Coverage Rather than a magic overall coverage number, require explicit test coverage for: ```text tenant boundaries design approval inspection completion invoice issue payment/refund membership privilege changes clinical record signing/amendment when healthcare exists prescribing authorization when healthcare exists ``` ### Release Gate Principle No single metric such as: ```text 90% test coverage ``` is sufficient evidence of production readiness. Quality gates are based on critical behavior, not vanity percentages. --- ## 97. Final Design Position The platform is: ```text One Shared Platform │ ├── Shared Identity / Sessions ├── Shared Security / Authorization ├── Shared Documents ├── Shared Financial Core ├── Shared Audit / Outbox ├── Shared Jobs / Webhooks / Notifications │ ├── Engineering Product │ ├── Engineering Frontend │ ├── Engineering REST APIs │ ├── Engineering State Machines │ └── Engineering Tables │ ├── Legal Product │ ├── Legal Frontend │ ├── Legal REST APIs │ └── Legal Tables │ └── Healthcare Product ├── Healthcare Frontend ├── Healthcare REST APIs ├── Healthcare Security Policies └── Healthcare Tables ``` The system shares infrastructure where reuse is valuable while preserving independent domain semantics where professional workflows differ. v3 is the implementation baseline for the Engineering MVP. It is not, by itself, evidence of: ```text production certification regulatory compliance security certification performance at a specific scale ``` Those claims require implementation evidence, security testing, operational controls, measured load results, restore tests, and profession/jurisdiction-specific review. --- # v3 Changelog Compared with v2, v3 adds or changes: ```text ✓ corrected refresh-token family persistence ✓ PostgreSQL 18 UUIDv7 option ✓ engineering client contacts ✓ engineering design assignments ✓ cancelled vs withdrawn design semantics ✓ inspection outcome separate from lifecycle ✓ linked follow-up inspection workflow ✓ project budget domain model ✓ version-level document checksums ✓ document classification and categories ✓ upload policy and content validation ✓ webhook configuration and delivery model ✓ encrypted/recoverable webhook signing material ✓ async job architecture ✓ import/export as POST + 202 job creation ✓ at-least-once outbox semantics made explicit ✓ idempotent outbox consumers required ✓ property-based state-machine testing ✓ chaos tests for outbox/job processing ✓ separate tenant-attack and invariant-failure metrics ✓ milestone-based delivery planning ✓ critical-path production readiness gates ✓ no arbitrary code-coverage production target ```