LumisCare — Enterprise Healthcare Platform
Home health and community care management SaaS. Rebrand from VivaCareUSA. Stack: React 19, Java 21 Spring Boot, Azure.
- Rebrand Plan — VivaCareUSA → LumisCare
- Architecture — Component HLD Master
- Architecture — Event Bus Map
- Architecture — Frontend Route Map
- Fix Backlog — P0/P1/P2 Issues
- Mock-to-Real Migration Plan
- LumisCare Demo P0-Batch Closure — MC #102844
Rebrand Plan — VivaCareUSA → LumisCare
Plan: LumisCare Rebrand — VivaCareUSA → LumisCare
Architect: Petter Graff (Team Lead) Date: 2026-04-05 Estimated execution: 1 working day
Research Summary
Golden Commit
c80a3637 — "ALL TESTS GREEN: Element 100%, CEO 100%, Flow PASS" (2026-01-25)
This is the autocoder Day 1 commit: full project skeleton + 500 features registered in features.db.
No VivaCareUSA-branded code built yet — cleanest possible rebrand starting point.
411 commits of autocoder feature builds come after this. We rebrand the skeleton, then rerun autocoder.
What Needs Rebranding
- 106 files with VCC/vivacare references
- ~238 occurrences across .ts, .tsx, .js files
- Key areas:
- HTTP headers:
X-VCC-Organization-Id,X-VCC-User-Id→X-LC-Organization-Id,X-LC-User-Id - ENV vars:
VITE_VCC_ORGANIZATION_ID→VITE_LC_ORGANIZATION_ID - Mock emails:
@vivacare.com→@lumiscare.com - CSS comments:
VivaCareUSA Color System→LumisCare Color System - Azure DevOps script:
azureProject: 'VCC'→azureProject: 'LumisCare' - CSS legacy vars:
--color-vcc-*→--color-lc-*
- HTTP headers:
Design Tokens — Already Correct
index.css already has the right colors (Jade Green, Neon Blue, Mint Green).
Only comments and legacy var names need updating.
Infrastructure — Already Exists
Azure Container Apps, Static Web Apps, Bicep IaC all deployed.
deploy-frontends.sh + deploy-demo.sh exist and work.
The Core Problem
10 days of localStorage migrations, BFF wiring, header standardization — none of it was requested. The golden commit worked. We restore it, rebrand it, ship it.
Objective
Branch off c80a3637 (project skeleton, 500 features in DB, zero built code),
rebrand all skeleton files (VivaCareUSA/VCC → LumisCare/LC),
then run AutoCoder to rebuild all 500 features with LumisCare branding from day 1.
No migrations. No patching. Clean rebuild.
Team Orchestration
Team Members
| ID | Name | Role | Agent |
|---|---|---|---|
| B1 | branch-builder | Create clean rebrand branch from golden commit | builder |
| V1 | branch-validator | Verify branch is clean and builds | validator |
| B2 | rebrand-builder | Execute mechanical find-replace rebrand | codecraft |
| V2 | rebrand-validator | Verify zero VCC references remain, app builds | validator |
| B3 | design-builder | Update CSS legacy var names + comments only | builder |
| V3 | design-validator | Verify design tokens are correct, no visual regression | validator |
| B4 | deploy-builder | Deploy to Azure dev environment | flowforge |
| V4 | deploy-validator | Playwright smoke test on deployed URL | sentinel-tester |
Step-by-Step Tasks
Phase 1: Restore Golden State
Task 1.1 — Create rebrand branch from c80a3637 skeleton
- Owner: B1 (builder)
- BlockedBy: none
- Command:
cd ~/projects/client/lumiscare git checkout c80a3637 -b rebrand/lumiscare-clean git push origin rebrand/lumiscare-clean - Acceptance:
- Branch
rebrand/lumiscare-cleanexists on origin -
git log -1shows commitc80a3637 - Project skeleton files present (frontend/, backend/, infrastructure/)
- features.db contains 500 features with passes=0
- Branch
Task 1.2 — Validate skeleton compiles
- Owner: V1 (validator)
- BlockedBy: 1.1
- Commands:
cd ~/projects/client/lumiscare/frontend/web && yarn install && yarn type-check sqlite3 features.db "SELECT COUNT(*) FROM features WHERE passes=0;" # Expected: 500 - Acceptance:
- TypeScript exits 0 (or known baseline errors only)
- 500 features in DB, all passes=0 (ready for autocoder)
- No built feature code — only skeleton
Phase 2: Mechanical Rebrand
Task 2.1 — Execute find-replace rebrand
-
Owner: B2 (codecraft)
-
BlockedBy: 1.2
-
Replacements (exact, case-sensitive where needed):
Find Replace Scope VivaCareUSALumisCareall src files VivaCare USALumisCareall src files vivacareusalumiscareall src files vivacare.comlumiscare.comall src files X-VCC-Organization-IdX-LC-Organization-Id.ts/.tsx X-VCC-User-IdX-LC-User-Id.ts/.tsx VITE_VCC_ORGANIZATION_IDVITE_LC_ORGANIZATION_ID.ts/.tsx/.env VITE_VCC_USER_IDVITE_LC_USER_ID.ts/.tsx/.env lumiscare-organization-idlumiscare-organization-idNO CHANGE (already correct) --color-vcc---color-lc-.css files VCC GreenLC Greencomments VCC BlueLC Bluecomments azureProject: 'VCC'azureProject: 'LumisCare'scripts -
Acceptance:
-
grep -r "VivaCareUSA\|vivacareusa\|vivacare.com\|X-VCC-\|VITE_VCC_" frontend/web/srcreturns 0 results -
yarn buildstill passes after replacements
-
Task 2.2 — Validate zero VCC references
- Owner: V2 (validator)
- BlockedBy: 2.1
- Commands:
grep -rn "VivaCareUSA\|VivaCare USA\|vivacareusa\|vivacare\.com\|X-VCC-\|VITE_VCC_" \ ~/projects/client/lumiscare/frontend/web/src/ | wc -l # Must return 0 grep -rn "--color-vcc-" ~/projects/client/lumiscare/frontend/web/src/ | wc -l # Must return 0 - Acceptance:
- VCC references = 0
- vivacare references = 0
- Build passes
Phase 3: Design Token Cleanup
Task 3.1 — Update CSS comments and legacy var names
- Owner: B3 (builder)
- BlockedBy: 2.2
- Files:
frontend/web/src/index.css— update comment header only- Any remaining
--color-vcc-*→--color-lc-*(if V2 missed any)
- Acceptance:
-
index.csscomment saysLumisCare Color System - No
VCCin any CSS file - Colors unchanged (Jade Green #35b276, Neon Blue #516df2, Mint #73ffd3)
-
Task 3.2 — Validate design tokens
- Owner: V3 (validator)
- BlockedBy: 3.1
- Acceptance:
-
--color-jade-500: #35b276present -
--color-blue-500: #516df2present -
--color-mint-500: #73ffd3present - No VCC color names remain
-
Phase 4: Deploy
Task 4.1 — Deploy to Azure dev environment
- Owner: B4 (flowforge)
- BlockedBy: 3.2
- Commands:
cd ~/projects/client/lumiscare # Push rebrand branch → triggers Azure Static Web Apps auto-deploy git push origin rebrand/lumiscare-clean # OR run manual deploy script bash infrastructure/deploy-frontends.sh dev - Acceptance:
- Frontend deployed to Azure Static Web Apps (dev)
- HTTP 200 on all 3 portal URLs
- No build errors in Azure DevOps pipeline
Task 4.2 — Smoke test deployed app
- Owner: V4 (sentinel-tester)
- BlockedBy: 4.1
- Test the 3 portals:
- Backoffice: login, dashboard load, clients list
- Admin: organizations page, users page
- Family Portal: dashboard, care plan
- Acceptance:
- All 3 portals load without JS errors
- Login flow works (or mock auth works in dev)
- No "VivaCare" or "VCC" text visible in UI
- "LumisCare" branding visible throughout
Validation Commands (Final)
# 1. Zero VCC references in frontend
grep -rn "VCC\|VivaCare\|vivacare" ~/projects/client/lumiscare/frontend/web/src/ \
| grep -v "//\|node_modules" | wc -l
# Expected: 0
# 2. Build passes
cd ~/projects/client/lumiscare/frontend/web && yarn build && yarn type-check
# 3. Portal URLs return 200
curl -s -o /dev/null -w "%{http_code}" https://jolly-bay-01cfa3003.6.azurestaticapps.net
curl -s -o /dev/null -w "%{http_code}" https://zealous-hill-097d4b803.6.azurestaticapps.net
curl -s -o /dev/null -w "%{http_code}" https://white-island-07c0e4c03.4.azurestaticapps.net
What This Plan Does NOT Do
- No localStorage migrations (P2-28 through P2-32 stay in MC for later)
- No new features
- No backend changes
- No BFF wiring changes
- No architectural refactoring
Restore → Rebrand → Deploy. That's it.
Risk
| Risk | Mitigation |
|---|---|
| Azure DevOps pipeline still named "VCC" | Rename pipeline display name only — do NOT touch infra resource names (breaking change) |
Backend services still use X-VCC-* headers |
Frontend and backend header rename must be done in same PR, or keep old header names and only rename in comments |
8b0effdc missing some later fixes |
V1 task verifies build — if broken, use next clean commit after 8b0effdc |
Note on headers: X-VCC-Organization-Id is sent by frontend AND read by backend services. If we rename on frontend only, backend breaks. Either rename both simultaneously, or leave headers as-is (they're internal API headers, not user-visible branding).
Execution
Run /build-plan to execute with agent teams.
Architecture — Component HLD Master
LumisCare — Component High-Level Design Master Document
Version: 1.0 Date: 2026-04-04 Author: Dr. Sarah Chen, Healthcare IT Systems Architect Status: Active — Reflects verified implementation state as of 2026-04-04 Method: Static analysis of source tree + HLD document review + gap analysis synthesis
Document Purpose
This document provides the authoritative component-level HLD for all LumisCare microservices, BFF layers, and frontend features. It cross-references the documented architecture (VCC2-C4-Architecture-Diagrams.md, Services-Integration-Matrix.md, BFF-Architecture-Design.md, Service-Bus-Subscriptions-Design.md) against verified implementation on disk.
Each component section records: implementation status, controller/entity counts, BFF integration status, event bus wiring status, and known gaps.
CRITICAL NOTE — Naming inconsistency: HLD documents use "VCC 2.0" throughout. The BFF design switches to "iCON". The deployed product is "LumisCare". All three names refer to the same system. This document uses "LumisCare" as the canonical product name.
System Architecture Summary
Web (React 19) ──────────────────────── Web BFF (port 8080)
│
Mobile (React Native / Expo SDK 52) ──── Mobile BFF
│
┌────────────────┴───────────────────┐
│ MICROSERVICES LAYER │
│ (Java 21, Spring Boot 3.4) │
│ │
│ identity-service │
│ assessment-service │
│ careplan-service │
│ visits-service │
│ hr-service │
│ scheduling-service │
│ incidents-service │
│ finance-service │
│ notification-service │
│ safety-service │
│ policy-service │
│ document-service │
│ fhir-adapter-service │
└─────────────────────────────────────┘
│
Azure Service Bus (domain-events topic)
Azure PostgreSQL (per-service databases)
Azure Redis (shared cache)
Azure Blob Storage (documents)
Multi-tenancy: Row-Level Security (RLS) on organisation_id across all service databases. Auth: Azure Entra ID (MSAL) — JWT pass-through from BFF to microservices. Service-to-service via Azure Managed Identity. Deployment: Azure Container Apps (AKS target). Azure Static Web Apps for frontend.
Component Status Legend
| Status | Meaning |
|---|---|
| PRODUCTION-READY | Controllers, entities, repositories, and migrations all present and verified |
| PARTIAL | Core entities and controllers present but key integration (event bus, BFF, mobile) absent |
| SKELETON | Directory exists, minimal or no Java source files |
| UNDOCUMENTED | Implemented in code but absent from all HLD documents |
| DESIGN-ONLY | Documented in HLD but not implemented in code |
Component Index
- Identity
- HR
- Visits
- Scheduling
- Care Plans
- Assessments
- Incidents
- Finance
- Notifications
- Safety
- Policy (AI/RAG)
- Document
- FHIR Adapter
- Web BFF
- Mobile BFF
1. Identity
Purpose
Manages users, organisations, service users, RBAC roles, consent records, PHI access audit, and GDPR data rights. Serves as the authentication enrichment layer for Azure Entra ID tokens — all services call identity-service to resolve user context from JWTs.
Backend Service
- Status: PRODUCTION-READY
- Controllers: 8 — ConsentController, OrganizationController, ServiceUserController, RoleController, AuthInternalController, DataRightsController, SafetyController, UserController
- Entities: 11 — UserEntity, ServiceUserEntity, RoleEntity, OrganizationEntity, ConsentEntity, EmergencyContactEntity, OrganizationSettingsEntity, PhiAccessAuditEntity, PhiAnomalyAlertEntity, DataRightsRequestEntity, DnacprAuditLogEntity
- Repositories: 10 — UserRepository, ServiceUserRepository, RoleRepository, OrganizationRepository, ConsentRepository, EmergencyContactRepository, PhiAccessAuditRepository, PhiAnomalyAlertRepository, DataRightsRequestRepository, DnacprAuditLogRepository
- Migrations: 14 Flyway migrations (V8: role permissions, V12: PHI access audit and anomaly tables)
- Key capabilities:
- Multi-tenant organisation isolation via RLS on organisation_id
- PHI access audit infrastructure (PhiAccessAuditEntity, PhiAnomalyAlertEntity) — HIPAA minimum-necessary enforcement at data layer
- DNACPR audit log repository (DnacprAuditLogEntity) — advance directive capture. NOTE: creates dual-domain ownership with assessment-service (see Gaps)
- DataRightsController — GDPR/UK GDPR data subject access request workflow
- ConsentController + ConsentEntity — consent lifecycle management
- AuthInternalController — Entra ID token enrichment (JWT → VCC claims)
- RoleController — RBAC role and permission management
Frontend
- Pages:
/admin/users,/admin/roles, auth pages (login, MFA, OTP) - Status: Real — users, roles, and auth pages implemented in
src/features/admin/users,src/features/admin/roles,src/features/admin/organizations
BFF Integration
- web-bff: UsersController, OrganizationsController — GET /users/{id}, GET /organizations/{id}, GET /service-users/{id}
- mobile-bff: No dedicated controller confirmed — identity resolution happens via JWT enrichment on every request
Event Bus
- Publishes: No event publisher confirmed in source
- Subscribes: Consumes
employee.createdandemployee.terminatedfrom HR (documented in Integration Matrix; HrEventPublisher exists in hr-service) - Status: NOT WIRED — no identity-service event publisher found; subscription to HR events not confirmed in source despite being documented
Gaps
- No event publisher — service user onboarding events (
serviceuser.created) are not published, preventing other services from reacting to new intake - DNACPR dual-domain ownership: DnacprAuditLogEntity in identity-service and DnacprOrderEntity in assessment-service have no confirmed synchronisation mechanism. For a resuscitation decision, this is clinically unacceptable — a DNACPR order cancelled in assessment-service may not be reflected in the identity audit log
- No confirmed break-glass emergency access pattern implementation (entity infrastructure exists but controller-level break-glass with audit is not confirmed)
- No event publisher means identity cannot signal account deactivation to scheduling-service for orphan appointment cleanup
2. HR
Purpose
Manages employee records, qualifications, training certifications, availability preferences, leave management, performance reviews, and equality/diversity data. Acts as the canonical source of carer workforce data for the scheduling-service.
Backend Service
- Status: PRODUCTION-READY
- Controllers: 12 — EmployeeController, EmployeeSearchController, EmployeeNoteController, AvailabilityPreferencesController, TrainingRecordController, TrainingCertificateUploadController, EducationTrainingController, PerformanceReviewController, LeaveManagementController, SupportingDocumentController, HealthInformationController, ClientFeedbackController
- Entities: 12+ — EmploymentHistory, PerformanceReview, LeaveRecord, TrainingRecord, EducationTraining, CertificateUploadTracking, ResumeUploadTracking, HealthInformation, EqualityDiversity, NextOfKin, EmployeeReference, AvailabilityPreferences
- Repositories: 12 — EmployeeRepository, LeaveRecordRepository, PerformanceReviewRepository, TrainingRecordRepository, EducationTrainingRepository, AvailabilityPreferencesRepository, CertificateUploadTrackingRepository, ResumeUploadTrackingRepository, HealthInformationRepository, ClientFeedbackRepository, EmployeeReferenceRepository, SupportingDocumentRepository
- Migrations: 24 Flyway migrations (highest in the fleet — V14: leave records, V15: performance reviews)
- Key capabilities:
- HealthInformation as separate entity — appropriate PHI boundary for staff health data
- CertificationExpiryScheduler — scheduled job for expired training certificate detection (CQC Regulation 17 compliance)
- HrEventPublisher — publishes HR events to Azure Service Bus
- AvailabilityPreferencesService — publishes availability changes consumed by scheduling-service
- LeaveManagementService — publishes leave approval events
- Bradford Factor calculation support
Frontend
- Pages:
/employees(list),/employees/:id(detail), HR sub-pages for DBS, Training, Bradford Factor, Supervisions - Feature directory:
src/features/backOffice/hr/ - Status: Real — hr feature has full component structure (api, components, constants, hooks, models, pages, schemas, services, store, utils)
BFF Integration
- web-bff: EmployeeController, HrComplianceController, ResumeUploadController — GET /employees, GET /employees/{id}/availability, GET /employees/{id}/compliance
- mobile-bff: No dedicated HR controller — carer profile accessed via CarerScheduleController
Event Bus
- Publishes:
employee.created,employee.terminated,availability.updated,leave.approved,skill.added - Subscribes: Nothing confirmed (HR is primarily a publisher; scheduling-service consumes its events)
- Status: WIRED — HrEventPublisher, AvailabilityPreferencesService, LeaveManagementService all confirmed in source. AvailabilityUpdatedEventListener confirmed in scheduling-service as consumer.
Gaps
- Entity naming inconsistency: HR entities do not use the
*Entity.javasuffix convention used by other services. This does not affect runtime but complicates static analysis tooling and cross-service code navigation - ClientFeedbackController sits in hr-service — client feedback data may belong in assessment-service or incidents-service by domain
- No BFF controller for employee DBS (Disclosure and Barring Service) record status — DBS expiry is a CQC compliance item and should be surfaced to care coordinators via a dedicated endpoint
3. Visits
Purpose
Manages the full visit execution lifecycle: Electronic Visit Verification (EVV) check-in/check-out, care note recording, eMAR (electronic medication administration record), vital sign capture, task completion, observation recording, real-time carer location tracking, and geofence enforcement. The most clinically active service during field operations.
Backend Service
- Status: PRODUCTION-READY
- Controllers: 12 — VisitController, VisitLifecycleController, EVVController, EmarController, MedicationController, MedicationRefusalController, TaskController, ObservationController, VitalSignController, CareNoteController, GeofenceController, TrackingController
- Entities: 13+ — EVVRecord, EmarRecord, MedicationSchedule, MedicationAdministration, VisitTask, VitalSignRecord, TrackingSession, Geofence, Observation, ObservationThreshold, ObservationType, TaskStatus, MedicationRefusalAlert
- Repositories: 12 — VisitRepository, EVVRecordRepository, EmarRecordRepository, MedicationScheduleRepository, MedicationAdministrationRepository, MedicationRefusalAlertRepository, VitalSignRecordRepository, CareNoteRepository, CarerLocationRepository, TrackingSessionRepository, VisitTaskRepository, ObservationRepository
- Migrations: 7 Flyway migrations (V9: observation threshold table, V13: PRN minimum interval hours)
- Key capabilities:
- EVV 6-data-element compliance (21st Century Cures Act): service type, carer ID, service user ID + Medicaid ID, start/end GPS coordinates, transmission status with timestamp, manual override with reason
- TransmissionStatus enum (PENDING/TRANSMITTED) with EVVService aggregator submission pipeline
- MedicationAdministration with requiresWitness, witnessedBy, witnessSignatureUrl — controlled substance double-check protocol at entity level
- V13 migration: prn_min_interval_hours — PRN medication safety interval enforcement
- MedicationRefusalController + MedicationRefusalAlertRepository — refusal recording and alerting
- GeofenceController — service user address boundary enforcement
- TrackingController — real-time carer location (30-second intervals to Redis + Azure Web PubSub for dashboard)
Frontend
- Pages:
/visits(list),/visits/:id(detail), live tracking dashboard, geofence configuration, visit handoff - Feature directory:
src/features/backOffice/visits/with subdirectories: api, components, geofence, handoff, hooks, liveTracking, pages, services, store, types, utils - EVV feature:
src/features/backOffice/evv/— EVVPage, EVVComplianceReport, EVVRecordsList, EVVManualCheckInForm, EVVRecordDetail, EVVFilters - Status: Real — full component structure confirmed
BFF Integration
- web-bff: VisitsController, HandoffController, CompletedEventsController — GET /visits, GET /visits/{id}/full-details (aggregates visit + care plan tasks + service user + carer), GET /completed-events
- mobile-bff: CarerScheduleController (MobileVisitsClient) — GET /my-visits/today, POST /visits/{id}/check-in, POST /visits/{id}/check-out, POST /visits/{id}/tasks/{taskId}/complete, POST /tracking/location
- CRITICAL GAP: Mobile BFF has no eMAR controller, no EVV dedicated controller, no vital signs controller, and no care note controller. If mobile carers access these endpoints, they bypass the BFF layer entirely.
Event Bus
- Publishes:
visit.checkedin,visit.completed,task.flagged,checkin.timeout,assistance.requested,location.updated,redflag.detected(VisitEventPublisher confirmed in source) - Subscribes: No subscription confirmed (documented as publisher-only in MVP Integration Matrix)
- Status: PARTIALLY WIRED — VisitEventPublisher exists; however, no consumer-side listeners confirmed in finance-service for
visit.completed(the event that should trigger invoice line item creation)
Gaps
- Only 7 Flyway migrations for a service with 12 controllers and significant clinical data scope — schema change audit trail is incomplete; later entity changes may have been applied via ORM DDL rather than versioned migrations (compliance risk for schema change governance)
- No confirmed barcode/NFC medication verification integration at the EmarController layer — entity exists but scan endpoint not confirmed
- Mobile BFF missing: eMAR recording, EVV check-in/check-out (dedicated), vital sign capture, incident reporting, and SOS — if mobile app calls these directly (bypassing BFF), JWT scope enforcement, PHI boundary logging, and API gateway rate limiting are not applied consistently (HIPAA minimum-necessary enforcement gap)
- Controlled substance witness enforcement: MedicationAdministration has the correct entity fields (requiresWitness, witnessedBy, witnessSignatureUrl) but business logic enforcement preventing "given" status without a witness signature needs to be confirmed at EmarController service layer — entity-level fields are necessary but not sufficient
- Finance-service has no confirmed listener for
visit.completedevent — invoicing cannot be triggered automatically from visit completion
4. Scheduling
Purpose
Creates, manages, and publishes care schedules. Handles recurring appointment generation, carer assignment with skill matching, schedule approval workflow, cover arrangements, and proposed appointment review. Bridges care plan proposed visits and operational visit execution.
Backend Service
- Status: PRODUCTION-READY
- Controllers: 9 — AppointmentController, RecurringScheduleController, ScheduleController, ScheduleApprovalController, ScheduleAlertController, ScheduleEventController, CoverController, ProposedAppointmentReviewController, FinanceController
- Entities: 7 — AppointmentEntity, RecurringScheduleEntity, ScheduleGenerationEntity, ScheduleAlertEntity, ScheduleEventEntity, CarePlanEventIdempotencyEntity, HrAvailabilityEventIdempotencyEntity
- Repositories: 7 — AppointmentRepository, RecurringScheduleRepository, ScheduleGenerationRepository, ScheduleAlertRepository, ScheduleEventRepository, CarePlanEventIdempotencyRepository, HrAvailabilityEventIdempotencyRepository
- Migrations: 13 Flyway migrations (V9: travel time on appointments, V13: rejected status on schedule generations)
- Key capabilities:
- Two-step scheduling governance: ScheduleApprovalController + ProposedAppointmentReviewController
- Idempotency repositories (CarePlanEventIdempotency, HrAvailabilityEventIdempotency) — duplicate event delivery from care plan or HR will not create duplicate appointments
- AppointmentEventPublisher — outbound event publishing for schedule lifecycle events
- HrAvailabilityUpdatedEventHandler — consumes HR events to keep schedules consistent with carer availability
- FinanceController within scheduling-service — feeds billable hours data to finance-service
- Travel time field (V9 migration) — routing time included in appointment planning
- ScheduleAlertController + ScheduleAlertEntity — alert generation for scheduling conflicts
Frontend
- Pages:
/scheduling(week/month view), appointment detail, cover requests, schedule approval workflow - Feature directory:
src/features/backOffice/scheduling/with subdirectories: components, pages, services, types - Status: Real — confirmed component structure
BFF Integration
- web-bff: SchedulingController, CalendarController — GET /schedules, POST /schedules, PUT /schedules/{id}/publish, GET /calendar (aggregated view)
- mobile-bff: AppointmentsController (AppointmentsService via scheduling-service) — GET /my-appointments, GET /appointments/{id}
Event Bus
- Publishes:
schedule.published,schedule.updated(AppointmentEventPublisher confirmed) - Subscribes:
availability.updated,leave.approvedfrom hr-events (HrAvailabilityUpdatedEventHandler confirmed with idempotency);careplan.publishedfrom careplan-events (CarePlanEventIdempotencyEntity present — idempotency ready, but no publisher on careplan-service side) - Status: PARTIALLY WIRED — publisher confirmed; HR subscription confirmed (fully wired); careplan.published subscription is idempotency-ready but the publisher (careplan-service) does not exist
Gaps
- ScheduleAlertEntity has AlertType and AlertSeverity enums but no confirmed integration path to notification-service for alert delivery — scheduling alerts may not reach care managers
- Travel time (V9 migration) is stored but no routing API integration confirmed — travel time is entered manually, not calculated from a routing service
- CarePlanEventIdempotencyEntity exists and is ready to consume careplan.published — but the careplan-service has no event publisher, so this idempotency infrastructure is currently unused
- No confirmed mobile BFF endpoint for cover requests or schedule change notifications to carers
5. Care Plans
Purpose
Manages the full care plan lifecycle: AI-assisted generation from assessments, plan versioning, task definition, master visit type configuration, and proposed visit generation. The care plan is the primary clinical document linking assessment outcomes to scheduled care delivery.
Backend Service
- Status: PRODUCTION-READY
- Controllers: 5 — CarePlanController, AiGenerationController, MasterTaskController, MasterVisitTypeController, CarePlanVersionHistoryController
- Entities: 8 — CarePlan, CarePlanVersion, MasterVisitType, MasterTaskType, MasterTaskCategory, TaskDefinition, ProposedVisit, AiMetrics
- Repositories: 8 — CarePlanRepository, CarePlanVersionRepository, MasterVisitTypeRepository, MasterTaskCategoryRepository, MasterTaskRepository, TaskDefinitionRepository, ProposedVisitRepository, AiMetricsRepository
- Migrations: 10 Flyway migrations (V2: care plan version table, V4: master task type table)
- Key capabilities:
- Care plan versioning: CarePlanVersion entity + CarePlanVersionHistoryController — full version history with diff capability
- Approval-to-publish workflow: CarePlan has status, publishedAt, publishedBy fields
- AI-assisted care plan generation: AiGenerationController + AiMetricsRepository — instrumented for outcome tracking
- ProposedVisit entity — proposed visit schedule generated from care plan tasks, consumed by scheduling-service
- MasterVisitType and MasterTaskType — configurable task library per organisation
Frontend
- Pages:
/carePlans(list),/carePlans/:id(detail with version history), AI care plan generator (/ai/care-plan-generator) - Feature directory:
src/features/backOffice/carePlans/(components, pages, services),src/features/backOffice/ai/AICarePlanGeneratorPage.tsx - Status: Real — confirmed component structure
BFF Integration
- web-bff: CarePlanController (BFF) — GET /care-plans/{id}, POST /care-plans/generate, GET /care-plans/{id}/version-history
- mobile-bff: CarePlanController (mobile) — GET /care-plans/{id} (read-only, for visit execution context)
- Generated client: assessment-client and careplan-client are generated OpenAPI clients in both BFFs
Event Bus
- Publishes:
careplan.published,careplan.updated— DOCUMENTED in C4 diagram and Integration Matrix but NO event publisher class found in careplan-service source - Subscribes:
assessment.completed— DOCUMENTED as trigger for AI generation but NO subscription listener found in careplan-service source - Status: NOT WIRED — neither publisher nor subscriber is implemented. This is the most critical clinical workflow gap in the platform: the Assessment → Care Plan → Schedule chain has broken links at both ends of careplan-service
Gaps
- No event publisher for
careplan.published— scheduling-service cannot pick up a newly published care plan automatically (CarePlanEventIdempotencyEntity in scheduling-service is ready but has no events to consume) - No subscription listener for
assessment.completed— AI care plan generation must be triggered manually rather than automatically on assessment completion - No RiskAssessment entity within careplan-service — risk assessments are in assessment-service (appropriate) but linkage/reference between care plan and risk assessment outcomes is not confirmed
- No countersignature or RN approval workflow entity visible at care plan level (publishedBy exists but role constraint enforcement at controller level not confirmed)
- AiMetrics entity tracks generation but no confirmed outcome feedback loop (care plan quality improvement over time requires feedback from visit task completion data)
6. Assessments
Purpose
Manages the complete clinical assessment lifecycle: intake, section/question/answer management, risk assessments (NEWS2, Waterlow, PHQ-9, GAD-7), Mental Capacity Assessment (MCA), DNACPR order management, medication reconciliation, clinical scoring, AI-assisted assessment transcription, and real-time collaborative assessment via Azure Web PubSub.
Backend Service
- Status: PRODUCTION-READY — highest controller count in the fleet
- Controllers: 23 — AssessmentIntakeApiController, AssessmentStatusApiController, AssessmentQuestionsApiController, AssessmentAnswersApiController, AssessmentSectionsApiController, AssessmentSubsectionsApiController, AssessmentMembersApiController, AssessmentSubmissionApiController, AssessmentDashboardApiController, RiskAssessmentSectionsApiController, RiskQuestionsApiController, RiskAnswersApiController, MentalCapacityAssessmentApiController, ClinicalScoringController, DnacprOrderApiController, MedicationsApiController, ObservationsApiController, NotesController, PhotosController, PatientsApiController, AiMessageController, WebPubSubController, VersionController
- Entities: 14+ — AssessmentEntity, News2ScoreEntity, WaterlowScoreEntity, Phq9ScoreEntity, MentalCapacityAssessmentEntity, DnacprOrderEntity, MedicationEntity, QuestionTemplateEntity, RiskSectionTemplateEntity, RiskQuestionTemplateEntity, MedicalRiskAuditEntity, EmergencyContactEntity, ConsentTypeLabelEntity, AssessmentAnswerEntity
- Repositories: 14+ — AssessmentRepository, AssessmentAnswerRepository, WaterlowScoreRepository, MentalCapacityAssessmentRepository, DnacprAcknowledgementRepository, ResponseRepository, SectionRepository, SectionTemplateRepository, SubsectionTemplateRepository, QuestionTemplateRepository, MedicationRepository, PhotoRepository, CareNeedRepository, ConsentTypeRepository
- Migrations: 8 Flyway migrations (V1: create tables, V5: add note columns)
- Key capabilities:
- NEWS2 (National Early Warning Score 2) — deterioration detection
- Waterlow score — pressure injury risk
- PHQ-9 — depression screening (IAPT-compatible)
- MentalCapacityAssessmentApiController + MentalCapacityAssessmentEntity — Mental Capacity Act 2005 compliance
- DnacprOrderApiController + DnacprOrderEntity + DnacprAcknowledgementRepository — resuscitation decision management with acknowledgement audit
- ClinicalScoringController — automated score calculation for standardised tools
- AiMessageController — AI-assisted assessment transcription (Azure OpenAI)
- WebPubSubController — real-time collaborative assessment (multi-assessor support)
- MedicalRiskAuditEntity — audit trail for risk assessment changes
- PhotosController — photo evidence capture for wound/pressure injury documentation
Frontend
- Pages: Assessment list, assessment detail, section-by-section questionnaire, risk assessment views, MCA assessment, clinical scoring summaries
- Feature directory:
src/features/backOffice/clients/(assessment workflows are accessed via client record) - Status: Real — clients feature has full component structure including assessment workflows
BFF Integration
- web-bff: BffAssessmentController, AssessmentWebDashBoardController — GET /assessments/{id}, POST /assessments, GET /assessments/dashboard
- mobile-bff: AssessmentsController, RiskAssessmentsController (assessment-client generated OpenAPI client) — GET /assessments/{id}, POST /assessments/submit
- Generated client: assessment-client confirmed as generated OpenAPI client in both BFFs
Event Bus
- Publishes:
assessment.completed— DOCUMENTED as trigger for care plan AI generation but NO event publisher class found in assessment-service source - Subscribes: Nothing documented
- Status: NOT WIRED — assessment.completed is the first event in the primary clinical workflow chain (Assessment → CarePlan → Schedule → Visit → Finance). Its absence breaks the entire automated care pathway.
Gaps
- No event publisher — assessment.completed cannot trigger automated care plan generation
- 8 Flyway migrations for 23 controllers is critically low — high probability that schema changes have been applied outside of Flyway migrations, breaking the schema change audit trail required for clinical system governance
- DNACPR dual-domain: DnacprOrderEntity lives here (correct domain) but DnacprAuditLogEntity lives in identity-service with no confirmed sync mechanism
- No DoLS (Deprivation of Liberty Safeguards) entity confirmed — DoLS authorisation tracking is a legal requirement for any service user subject to a standard or urgent DoLS authorisation
- GAD-7 (anxiety screening) not confirmed as a named entity despite PHQ-9 being present — may be handled via generic question templates
7. Incidents
Purpose
Manages incident reporting, safeguarding concerns, hazard logging, complaints management, and CQC statutory reporting evidence. Provides CSV export for regulatory data extraction during CQC inspections.
Backend Service
- Status: PRODUCTION-READY
- Controllers: 7 — AccidentIncidentController, SafeguardingController, HazardController, ComplaintController, LogActionController, SummaryController, CsvExportController
- Entities: 9 — AccidentIncidentLog, SafeguardingLog, HazardLog, ComplaintLog, LogAction, LogStatus, LogStatusConverter, SeverityLevel, SeverityLevelConverter
- Repositories: 5 — AccidentIncidentLogRepository, SafeguardingLogRepository, HazardLogRepository, ComplaintLogRepository, LogActionRepository
- Migrations: 9 Flyway migrations (V4: hazard logs, V7: outbox tables for event publishing)
- Key capabilities:
- SafeguardingLog with reportedToLocalAuthority, localAuthorityReference, reportedToPolice, policeReference — statutory reporting data model at entity level
- SeverityLevel entity with converter — structured severity classification
- CsvExportController — regulatory data extraction for CQC inspection evidence
- SummaryController — aggregate incident reporting across categories
- V7 outbox migration — reliable event publishing via transactional outbox pattern
- LogAction + LogActionRepository — action tracking against each incident
Frontend
- Pages:
/incidents(list with filters), incident detail, safeguarding view, incident creation forms - Feature directory:
src/features/backOffice/incidents/with subdirectories: components, detail, hooks, list, safeguarding, services - Status: Real — full component structure with dedicated safeguarding subdirectory
BFF Integration
- web-bff: IncidentsController — GET /incidents, POST /incidents, GET /incidents/{id}, GET /safeguarding/{id}
- mobile-bff: No incidents controller confirmed — mobile carers cannot report incidents through the BFF layer (PHI boundary gap)
Event Bus
- Publishes:
incident.created(EventPublisher confirmed in source; outbox tables confirmed in V7 migration) - Subscribes: Nothing documented
- Status: PARTIALLY WIRED — publisher infrastructure confirmed (outbox tables + EventPublisher class). Notification-service should consume incident.created for safeguarding alerts, but @ServiceBusListener is commented out in notification-service (see Notifications component)
Gaps
- No mandatory reporting deadline field — CQC requires notification within 24 hours for certain incident types (death of service user, serious injuries, allegations of abuse). No notificationDeadline or overdueNotification field confirmed. No automated deadline enforcement or escalation for overdue statutory notifications
- SafeguardingController endpoint mapping not confirmed — routes may be implemented or stubbed; direct inspection required
- No MASH (Multi-Agency Safeguarding Hub) integration endpoint or referral number tracking beyond localAuthorityReference
- Mobile BFF missing incidents reporting controller — field carers cannot submit incident reports through the secured BFF layer
- No CQC RI (Responsible Individual) notification workflow entity — RI must be notified for certain CQC Regulation 18 events; this is not automated
- No confirmed near-miss capture category (distinct from accidents) — near-miss data is required for CQC Well-Led evidence
8. Finance
Purpose
Manages the complete financial lifecycle: invoice generation, pay rates, payroll, timesheet management, insurance claim generation (837P/837I EDI), remittance reconciliation (835 EDI), gross profit analysis, hours variance reporting, and payer management. The largest service by code volume in the fleet.
Backend Service
- Status: PRODUCTION-READY
- Controllers: 11 — PayRatesController, PayrollsController, DashboardController, GrossProfitController, InvoiceGroupsController, InvoiceRatesController, PaymentGroupsController, InvoicesController, HoursVarianceController, InsuranceClaimsController, PayerManagementController
- Entities: 14 — Invoice, InvoiceGroup, ServiceUserInvoiceGroup, InvoicePayment, Timesheet, TimesheetLineItem, TimesheetBatchJob, PaymentGroup, FinanceDashboardMetric, FinanceAuditLog, OutboxEvent, InsuranceClaim, ClaimLineItem, RemittanceRecord
- Repositories: 10+ — InvoiceGroupRepository, InvoiceLineItemRepository, ServiceUserInvoiceGroupRepository, InvoicePaymentRepository, PaymentGroupRepository, PayRateRepository, InvoiceBatchJobRepository, TimesheetsRepository, FinanceSequenceRepository, OutboxEventRepository
- Migrations: 17 Flyway migrations (V1: create tables, V12: unique index on invoice rates)
- Key capabilities:
- 837P/837I EDI billing pathway: InsuranceClaim, ClaimLineItem entities, InsuranceClaimsController, PayerManagementController
- 835 remittance processing: RemittanceRecord entity, Edi835ProcessorService, RemittanceProcessResponse DTO — full claim-to-remittance cycle
- Transactional outbox pattern: OutboxEvent entity + OutboxEventRepository — the infrastructure exists for reliable event publishing, but the publisher dispatching to Service Bus is absent
- TimesheetBatchJob — async batch processing for payroll
- HoursVarianceController + GrossProfitController — operational finance analytics
- FinanceDashboardMetric entity — aggregated finance KPIs
Frontend
- Pages:
/finance(billing, payroll views), invoice list/detail, timesheet views, pay rates configuration - Feature directory:
src/features/backOffice/finance/(api, billing, components, definitions, hooks, models, pages, services, store, types, utils),src/features/backOffice/billing/ - Status: Real — full component structure confirmed
BFF Integration
- web-bff: FinanceBffController (FinanceClientConfig — WebClient, not generated OpenAPI client) — GET /invoices, GET /timesheets, GET /finance/dashboard, GET /gross-profit
- mobile-bff: No finance controller — appropriate for field worker role (carers do not access finance)
- Family portal: FamilyPortalController includes invoices endpoint (read-only GET confirmed)
Event Bus
- Publishes:
invoice.created,invoice.paid,timesheet.created— DOCUMENTED but OutboxEvent entity exists without a publisher class dispatching to Service Bus. The outbox pattern is initiated but incomplete. - Subscribes:
visit.completed— DOCUMENTED as trigger for invoice line item generation but no consumer listener confirmed in finance-service source - Status: NOT WIRED — the transactional outbox infrastructure is started (entity + repository) but the publisher component dispatching messages to Azure Service Bus is absent. Finance events will not fire. The visit.completed subscription is also absent — invoicing cannot be triggered automatically.
Gaps
- OutboxEvent + OutboxEventRepository present but no OutboxEventPublisher class — the transactional outbox is half-implemented; payroll discrepancies and invoice creation events will be silent
- No confirmed listener for visit.completed — visit-to-invoice automation is broken
- Non-standard package namespace: com.lumiscare.icon.finance (inconsistent with com.lumiscare.* fleet pattern) — suggests this service was imported or adapted from a separate codebase; introduces dependency drift risk
- NHS framework invoicing (NHSmail purchase order workflow, ICB/CCG rate card configuration) not confirmed — if this platform targets the UK NHS market, this is a billing gap
- No ANSI X12 generator class confirmed for outbound 837 claim generation (only inbound 835 processor confirmed)
9. Notifications
Purpose
Multi-channel notification dispatch: push (Firebase Cloud Messaging), email (SendGrid), SMS (Twilio), and in-app inbox. Consumes domain events from Azure Service Bus and routes them to appropriate users based on notification preferences and rate limits. Maintains notification history and delivery analytics.
Backend Service
- Status: PARTIAL — CRITICAL GAP
- Controllers: 5 — DeviceController, InboxController, NotificationPreferencesController, NotificationAnalyticsController, SosNotificationController
- Entities: 6 — DeviceRegistration, InAppNotification, NotificationPreference, NotificationTemplate, NotificationLog, NotificationBatchQueue
- Repositories: 6 — DeviceRegistrationRepository, InAppNotificationRepository, NotificationPreferenceRepository, NotificationTemplateRepository, NotificationLogRepository, NotificationBatchQueueRepository
- Migrations: 4 Flyway migrations (V1: initial schema, V4: notification batch queue)
- Key capabilities:
- ServiceBusConfig — Azure Service Bus connectivity configured
- ServiceBusEventListener — exists with handleDomainEvent method
- NotificationEventPublisher — outbound notification.sent/failed events
- NotificationBatchQueue — batch notification infrastructure
- SosNotificationController — direct-call SOS path (bypasses event bus; works independently)
- NotificationPreferencesController — per-user channel preference management
- DeviceController — FCM device token registration
- NotificationAnalyticsController — delivery tracking and metrics
Frontend
- Pages: Notification inbox, notification preferences
- Feature directory:
src/features/notifications/(api, components, hooks, pages, services, store, types) - Status: Real — full component structure confirmed
BFF Integration
- web-bff: NotificationsController — GET /notifications, PUT /notifications/{id}/acknowledge
- mobile-bff: No dedicated notifications controller confirmed — mobile notification delivery is via push (FCM) direct
Event Bus
- Publishes:
notification.sent,notification.failed(NotificationEventPublisher confirmed) - Subscribes: visit-events (checkin.timeout, task.flagged), schedule-events (schedule.published), finance-events (invoice.created, invoice.overdue), incidents-events (incident.created) — ALL DOCUMENTED. ServiceBusEventListener EXISTS but @ServiceBusListener annotation is COMMENTED OUT on line 86 of ServiceBusEventListener.java.
- Status: CRITICAL — service infrastructure is complete and service can send notifications via direct call. However, @ServiceBusListener is commented out. No domain events from any service are currently being consumed. Missed visit alerts, medication overdue alerts, safeguarding escalations, certification expiry, and schedule change notifications will not be delivered through the event bus pathway.
Gaps
- @ServiceBusListener commented out on ServiceBusEventListener.java line 86 — this is the single most impactful production gap in the entire platform. Uncommenting this line and validating end-to-end event delivery is the highest priority remediation action.
- Only 4 Flyway migrations for a service of this notification scope — migration history appears compressed
- No dead-letter queue handling or poison message retry strategy confirmed beyond the documented design
- No confirmed telephony (SMS Twilio) integration test
- No confirmed deduplication Redis key implementation (documented in design but not confirmed in source)
10. Safety
Purpose
Manages lone worker protection (LWP), SOS emergency alerts, and emergency contact management. The LWP timer service runs server-side (not dependent on carer device signal) and escalates overdue check-in sessions through a supervisor notification ladder. This service addresses the CQC requirement for lone worker safety in domiciliary care.
Backend Service
- Status: PRODUCTION-READY (Undocumented — no HLD entry exists)
- Controllers: 3 — SOSController, LWPController, EmergencyContactController
- Entities: 10 — LWPSession, LWPConfiguration, LWPCheckin, LWPEscalation, LWPSessionStatus, SOSEvent, SOSEventType, SOSNotification, EmergencyContact, ResolutionType
- Repositories: 7 — LWPSessionRepository, LWPConfigurationRepository, LWPCheckinRepository, LWPEscalationRepository, SOSEventRepository, SOSNotificationRepository, EmergencyContactRepository
- Migrations: 2 Flyway migrations (V10: safety tables, V11: add service_user_id to LWP sessions)
- Key capabilities:
- LWPTimerService — scheduled every 30 seconds, finds overdue LWP sessions, triggers escalation ladder (Level 1 to supervisor). Server-side execution — not dependent on carer device or mobile signal
- LWPEscalation entity + LWPEscalationRepository — persistent escalation audit trail
- SafetyEventPublisher — publishes LWP escalation events to Service Bus
- SOSController — emergency SOS event creation and notification dispatch
- EmergencyContact entity — direct outreach contact management
Frontend
- Pages: SOS alert banner on supervisor dashboard (
SOSAlertBanner.tsx), SOS history page (SOSHistoryPage.tsx), active visits map with SOS overlay (ActiveVisitsMap.tsx) - Feature directory:
src/features/backOffice/dashboard/(SOS components are embedded in supervisor dashboard, not a dedicated safety nav item) - Status: Partial — no dedicated safety nav item; SOS features are integrated into dashboard and notification areas
BFF Integration
- web-bff: No dedicated safety BFF controller confirmed — SOS alerts reach care managers via notification-service (SosNotificationController direct-call path)
- mobile-bff: No safety controller confirmed — mobile SOS trigger path through mobile BFF not confirmed (if mobile calls safety-service directly, BFF auth boundary is bypassed)
Event Bus
- Publishes: LWP escalation events (SafetyEventPublisher confirmed in source)
- Subscribes: Nothing documented
- Status: PARTIALLY WIRED — SafetyEventPublisher exists. SosNotificationController provides a direct-call path to notification-service for SOS alerts (this path works independently of Service Bus)
Gaps
- No dedicated safety navigation item in backOffice sidebar — lone worker protection and SOS are CQC-required features that should be discoverable by care managers and RI without searching the dashboard
- Level 2+ escalation (manager, Responsible Individual, emergency services) not confirmed — only Level 1 (supervisor) notification is confirmed in LWPTimerService
- No telephony fallback (IVR) for SOS trigger — mobile app dependency for SOS initiation is a risk if device is compromised, out of power, or carer is incapacitated
- Only 2 Flyway migrations for 10 entity types — schema was created in a single migration; schema change history is compressed
- Mobile BFF does not have a safety controller — mobile SOS trigger path is not secured through the BFF auth layer
- This service has no HLD document, no ADR, and no documented security posture — see ADR-009 stub created alongside this document
11. Policy (AI / RAG)
Purpose
Provides RAG (Retrieval-Augmented Generation) based policy question-and-answer capability. Care workers and managers can ask questions about organisational policies, CQC standards, and care protocols in natural language. The service chunks policy documents, stores vector embeddings, and queries them via Azure OpenAI.
Backend Service
- Status: PRODUCTION-READY (Undocumented — no HLD entry, no ADR)
- Controllers: 1 confirmed — PolicyRagController (text chunking, vector query)
- Entities: Policy document chunks, vector embeddings (exact entity names not confirmed in source grep — implementation uses Azure Cognitive Search or embedded vector store)
- Repositories: Not confirmed via static analysis
- Migrations: Not confirmed
- Key capabilities:
- PolicyRagController — handles natural language policy queries
- Text chunking pipeline — policy document preprocessing for vector storage
- Vector query — semantic search against policy document embeddings
- Azure OpenAI integration for response generation
Frontend
- Pages: AI feature directory exists (
src/features/backOffice/ai/AICarePlanGeneratorPage.tsx) but no dedicated policy search page confirmed - Status: Stub — ai feature directory contains care plan AI page only; policy search is not wired to a frontend page
BFF Integration
- web-bff: No BFF controller for policy RAG confirmed
- mobile-bff: No BFF controller for policy RAG confirmed
Event Bus
- Publishes: Nothing
- Subscribes: Nothing
- Status: NOT WIRED — policy-service is not in the Service Bus design
Gaps
- No HLD document, no ADR, no API contract documented
- No frontend page for policy search — the service is implemented but inaccessible to users
- No navigation item in backOffice sidebar
- No documented security posture — policy documents may contain sensitive operational information; access control for policy queries is not confirmed
- No BFF controller — if accessed directly, PHI boundary enforcement and rate limiting are bypassed
- See ADR-010 stub created alongside this document
12. Document
Purpose
Manages document storage, retrieval, and retention for clinical and operational documents. Uses Azure Blob Storage as the backing store with application-level retention policy enforcement. Supports document upload, categorisation, and time-limited access via pre-signed URLs.
Backend Service
- Status: PRODUCTION-READY (Undocumented — no HLD entry, no ADR)
- Controllers: 1 confirmed — DocumentController
- Entities: Document metadata entity (exact entity name not confirmed)
- Repositories: Document metadata repository (not confirmed via static analysis)
- Migrations: Not confirmed
- Key capabilities:
- DocumentController — document upload, download (pre-signed URL), list, delete
- Azure Blob Storage configuration — container management per document category
- Retention logic — configurable retention periods per document type
- Document categorisation — clinical vs. operational vs. HR documents
Frontend
- Pages: No frontend feature directory confirmed for document management
- Status: Not wired — no frontend feature, no navigation item
BFF Integration
- web-bff: No document BFF controller confirmed
- mobile-bff: BlobStorageController confirmed — handles file upload from mobile (likely used for assessment photos and CV upload, not full document management)
Event Bus
- Publishes: Nothing
- Subscribes: Nothing
- Status: NOT WIRED
Gaps
- No HLD document, no ADR, no API contract
- No frontend feature or navigation item — document management is inaccessible to care managers despite being implemented
- No confirmed integration with incidents-service for safeguarding document attachment
- No confirmed integration with hr-service for staff document management (separate SupportingDocumentController exists in hr-service — potential overlap)
- Retention logic exists but retention schedule per document category under UK GDPR and NHS Records Management Code of Practice is not documented
- Mobile BlobStorageController handles photo/file upload but may not route through DocumentController — document provenance tracking is unclear
- See ADR-011 stub created alongside this document
13. FHIR Adapter
Purpose
Exposes FHIR R4 resources (Patient, Observation, MedicationStatement) for interoperability with NHS systems, third-party care management platforms, and referral networks. Enables LumisCare to participate in NHS DSCR (Digitising Social Care Record) programme and GP Connect workflows.
Backend Service
- Status: PRODUCTION-READY (Undocumented — no HLD entry, no ADR)
- Controllers: 3+ — Patient FHIR provider, Observation FHIR provider, MedicationStatement FHIR provider (confirmed as FHIR R4 providers, not standard Spring controllers)
- Entities: FHIR resource mapping layer (not standard JPA entities — FHIR resources are mapped from LumisCare domain model)
- Repositories: Delegates to identity-service (Patient), assessment-service (Observation), visits-service (MedicationStatement)
- Migrations: None (FHIR adapter is a translation layer, no owned database)
- Key capabilities:
- FHIR R4 Patient resource — maps from ServiceUserEntity in identity-service
- FHIR R4 Observation resource — maps from assessment scores (NEWS2, Waterlow, PHQ-9) in assessment-service
- FHIR R4 MedicationStatement resource — maps from MedicationAdministration in visits-service
- NHS DSCR admin nav item exists (
src/features/admin/dscr/) — this is the only frontend reference to FHIR
Frontend
- Pages:
/admin/dscr(NHS DSCR configuration) - Feature directory:
src/features/admin/dscr/confirmed - Status: Partial — admin DSCR page exists; no FHIR resource viewer or API explorer for clinical staff
BFF Integration
- web-bff: No FHIR BFF controller confirmed
- mobile-bff: No FHIR BFF controller confirmed
Event Bus
- Publishes: Nothing
- Subscribes: Nothing
- Status: NOT WIRED
Gaps
- No HLD document, no ADR, no API contract — this is the highest security risk of the four undocumented services. FHIR R4 Patient and Observation resources expose PHI. If fhir-adapter-service is reachable without documented security controls, this is a GDPR and NHS DSPT data protection liability.
- No documented authentication model for FHIR endpoints — SMART on FHIR app authorisation (required for NHS GP Connect) is not confirmed
- No BFF controller — FHIR endpoints may be directly exposed without the aggregation, rate limiting, and PHI boundary logging that the BFF layer provides
- No confirmed CIS2 (NHS Care Identity Service 2) authentication integration — required for NHS-facing FHIR endpoints
- NHS DSCR admin page exists but is not connected to a live FHIR endpoint through a BFF
- See ADR-012 stub created alongside this document
14. Web BFF
Purpose
Backend-for-Frontend aggregation layer for the React 19 web application. Orchestrates parallel and sequential calls to microservices, aggregates responses, enforces JWT validation, applies rate limiting (via Azure API Management), and provides OpenAPI-generated typed client contracts. Port 8080.
Backend Service
- Status: PRODUCTION-READY
- Controllers: 22 — DashboardController, SupervisorDashboardController, SchedulingController, VisitsController, CarePlanController, ServiceUsersController, UsersController, EmployeeController, IncidentsController, BffAssessmentController, AssessmentWebDashBoardController, FamilyPortalController, HrComplianceController, FinanceBffController, CalendarController, CompletedEventsController, HandoffController, ReportsController, OrganizationsController, NotificationsController, ResumeUploadController, CqcReadinessController
- Generated OpenAPI clients: 4 of 10 services — assessment-client, careplan-client, hr-client, incidents-client
- WebClient-based (not generated): visits-service (VisitsController), scheduling-service (SchedulingController + CalendarController), finance-service (FinanceBffController + FinanceClientConfig), notification-service (NotificationsController + FamilyPortalController)
- Unintegrated services: safety-service (no dedicated BFF controller), policy-service (no controller), document-service (no controller), fhir-adapter-service (no controller)
- Key capabilities:
- FamilyPortalController confirmed read-only (GET patterns only, no POST/PUT/PATCH) — PHI minimum-necessary for family access
- CqcReadinessController — dedicated endpoint for CQC inspection evidence aggregation (clinically significant governance feature)
- DashboardController + SupervisorDashboardController — parallel microservice call aggregation for management views
- HrComplianceController — DBS, training certificate, and compliance expiry aggregation
Frontend Integration
- Target: All
src/features/backOffice/components,src/features/familyPortal/,src/features/admin/ - Current state (gap): Frontend CLAUDE.md reveals the web app currently calls the old Django monolith (
app-lumiscare-dev-uks-001.azurewebsites.net) through manual axios wrappers.src/api/generated/directory is scaffolded but contains no generated client files.yarn generate:apiis documented but not operational.
BFF Integration
- web-bff: IS the BFF — self
- mobile-bff: N/A
Event Bus
- Publishes: Nothing (BFF is stateless aggregation layer)
- Subscribes: Nothing
- Status: N/A
Gaps
- 6 of 10 microservices use WebClient with manual configuration rather than generated OpenAPI clients — contract drift risk as service APIs evolve
- safety-service, policy-service, document-service, and fhir-adapter-service have no BFF controller — these services are either inaccessible from the web frontend or are called directly (bypassing auth boundary)
- Frontend is currently routing to old Django monolith, not to web-bff — the BFF is built but not yet the live API target for the web app
- OpenAPI client generation pipeline (
yarn generate:api) is not operational — manual axios wrappers create inevitable contract drift at scale - No confirmed Redis caching layer in BFF for high-frequency identity lookups (documented as 24h TTL in Integration Matrix)
15. Mobile BFF
Purpose
Backend-for-Frontend aggregation layer for the React Native / Expo field worker mobile application. Provides a minimal, mobile-optimised API surface for care execution: care plan read access, appointment viewing, assessment submission, risk assessment, and blob storage for photos. Port not confirmed.
Backend Service
- Status: PARTIAL — HIGH RISK
- Controllers: 6 — CarePlanController, AppointmentsController, CarerScheduleController, AssessmentsController, RiskAssessmentsController, BlobStorageController
- Generated OpenAPI clients: 2 of 10 services — assessment-client, careplan-client
- WebClient-based: MobileVisitsClient (visits-service via CarerScheduleController and AppointmentsController)
- Key capabilities:
- AppointmentsController — GET /my-appointments, appointment detail
- CarerScheduleController — GET /my-visits/today, visit schedule
- AssessmentsController + RiskAssessmentsController — assessment submission for field assessments
- BlobStorageController — photo and document upload from mobile (assessment photos, CV upload)
- CarePlanController — read-only care plan access for visit execution context
Frontend Integration
- Target:
mobile/app/(React Native / Expo SDK 52) - Coverage: Care plan, assessments, appointments, visit schedule
BFF Integration
- web-bff: N/A
- mobile-bff: IS the BFF — self
Event Bus
- Publishes: Nothing
- Subscribes: Nothing
- Status: N/A
Gaps
- No eMAR controller — mobile carers cannot record medication administration through the BFF (HIPAA minimum-necessary enforcement gap if calling visits-service directly)
- No EVV dedicated controller — check-in/check-out is routed via CarerScheduleController/MobileVisitsClient but not through a dedicated EVV compliance controller with explicit 6-data-element capture
- No incidents controller — mobile carers cannot report incidents through the BFF
- No SOS / safety controller — mobile SOS trigger path is not secured through the BFF auth layer
- No vital signs controller — carer observation and vital sign recording during visits bypasses BFF
- No care note controller — care note recording during visits bypasses BFF
- Only 2 generated OpenAPI clients (assessment-client, careplan-client) — 8 services accessed without typed contracts
- No offline sync protocol confirmed at BFF layer — offline-first capability documented as a mobile feature but sync conflict resolution through BFF is not specified
Event Bus Summary Table
| Event | Producer | Publisher Code? | Consumer | Listener Code? | Status |
|---|---|---|---|---|---|
assessment.completed |
assessment-service | NO | careplan-service | NO | NOT WIRED |
careplan.published |
careplan-service | NO | scheduling-service | CarePlanEventIdempotencyEntity (ready) | NOT WIRED |
careplan.updated |
careplan-service | NO | visits-service | NO | NOT WIRED |
schedule.published |
scheduling-service | AppointmentEventPublisher YES | notification-service | @ServiceBusListener COMMENTED OUT | BROKEN |
schedule.updated |
scheduling-service | AppointmentEventPublisher YES | visits-service | NO | PARTIALLY WIRED |
visit.checkedin |
visits-service | VisitEventPublisher YES | notification-service | @ServiceBusListener COMMENTED OUT | BROKEN |
visit.completed |
visits-service | VisitEventPublisher YES | finance-service | NO | PARTIALLY WIRED |
task.flagged |
visits-service | VisitEventPublisher YES | notification-service | @ServiceBusListener COMMENTED OUT | BROKEN |
checkin.timeout |
visits-service | VisitEventPublisher YES | notification-service | @ServiceBusListener COMMENTED OUT | BROKEN |
assistance.requested |
visits-service | VisitEventPublisher YES | notification-service | @ServiceBusListener COMMENTED OUT | BROKEN |
invoice.created |
finance-service | OutboxEvent entity only — NO publisher | notification-service | @ServiceBusListener COMMENTED OUT | NOT WIRED |
invoice.paid |
finance-service | NO | notification-service | @ServiceBusListener COMMENTED OUT | NOT WIRED |
timesheet.created |
finance-service | NO | notification-service | @ServiceBusListener COMMENTED OUT | NOT WIRED |
employee.created |
hr-service | HrEventPublisher YES | identity-service | NO confirmed listener | PARTIALLY WIRED |
employee.terminated |
hr-service | HrEventPublisher YES | scheduling-service | NO | PARTIALLY WIRED |
availability.updated |
hr-service | HrEventPublisher YES | scheduling-service | AvailabilityUpdatedEventListener YES | WIRED |
leave.approved |
hr-service | HrEventPublisher YES | scheduling-service | NO confirmed listener | PARTIALLY WIRED |
incident.created |
incidents-service | EventPublisher YES (outbox) | notification-service | @ServiceBusListener COMMENTED OUT | BROKEN |
notification.sent |
notification-service | NotificationEventPublisher YES | analytics-service | NOT IMPLEMENTED | PARTIALLY WIRED |
Summary: HR → Scheduling (availability.updated) is the only fully wired event chain. The primary clinical workflow (Assessment → CarePlan → Scheduling → Visits → Finance) has broken links at every node.
Priority Remediation Register
P0 — Patient Safety and Compliance Blocking
P0-001: Uncomment @ServiceBusListener in notification-service
- File:
backend/services/notification-service/src/main/java/.../ServiceBusEventListener.javaline 86 - Impact: All event-driven alert delivery (missed visit, medication overdue, LWP escalation, safeguarding) is non-functional
- Effort: Minimal code change; requires end-to-end event delivery validation for all subscribed event types
P0-002: Resolve DNACPR dual-domain ownership
- DnacprOrderEntity in assessment-service + DnacprAuditLogEntity in identity-service with no sync mechanism
- Impact: Resuscitation decision ambiguity is clinically unacceptable
- Resolution: Either consolidate into assessment-service (correct domain) with identity-service holding a reference, or implement a confirmed event-driven sync pattern
P1 — Regulatory Exposure
P1-001: Add event publisher to assessment-service for assessment.completed
- Breaks the Assessment → CarePlan AI generation automated pathway
P1-002: Add event publisher to careplan-service for careplan.published
- Breaks the CarePlan → Scheduling automated pathway
P1-003: Add mobile BFF controllers for eMAR, EVV, incidents, and SOS
- Or document and formally enforce direct-service access security boundary with compensating controls
P1-004: Implement mandatory notification deadline and automated escalation in incidents-service
- CQC Regulation 18 requires notification within 24 hours for certain incident types
P1-005: Document and security-review fhir-adapter-service
- FHIR R4 Patient/Observation endpoints may expose PHI without documented authentication controls
- NHS DSPT and GDPR exposure
P2 — Architecture Integrity
P2-001: Implement OutboxEventPublisher in finance-service
- Completes the transactional outbox pattern already started
P2-002: Add finance-service listener for visit.completed event
- Enables automatic invoice line item generation from visit completion
P2-003: Confirm EmarController enforces witness requirement at service layer
- Entity fields exist; business logic enforcement must be verified
P2-004: Add Flyway migrations to bring visits-service (7) and assessment-service (8) in line with entity complexity
P2-005: Standardise finance-service package namespace from com.lumiscare.icon.finance to com.lumiscare.finance
P2-006: Operationalise yarn generate:api pipeline
- Without generated clients, frontend contract drift is inevitable as microservice APIs evolve
Architectural Risks
| Risk | Severity | Description |
|---|---|---|
| BFF-less frontend in production | CRITICAL | Frontend routes to old Django monolith, not web-bff. All microservices are bypassed. |
| Broken clinical workflow event chain | HIGH | Assessment → CarePlan → Schedule → Visit → Finance has multiple broken publisher/subscriber links |
| Notification service deaf to event bus | HIGH | @ServiceBusListener commented out — all event-driven alerts are non-functional |
| Four undocumented services in production path | HIGH | safety, policy, document, fhir-adapter — no API contracts, no security posture |
| FHIR PHI exposure | HIGH | FHIR R4 resources expose PHI without documented authentication controls |
| Finance event bus gap | MEDIUM | Transactional outbox started but publisher absent — billing events are silent |
| Mobile BFF coverage gap | HIGH | eMAR, EVV, incidents, SOS absent from mobile BFF — direct service calls bypass auth boundary |
| DNACPR dual ownership | HIGH | Resuscitation decision data in two services with no confirmed sync |
| CQC mandatory notification deadline | MEDIUM | No automated enforcement for 24-hour statutory reporting requirement |
| OpenAPI client drift | MEDIUM | 6 of 10 services accessed via manual WebClient — contract drift inevitable |
Document History
| Version | Date | Author | Changes |
|---|---|---|---|
| 1.0 | 2026-04-04 | Dr. Sarah Chen | Initial — 13 components + 2 BFFs documented. Evidence base: static filesystem analysis + HLD document review. Cross-reference with lumiscare-backend-lld.md and lumiscare-hld-gap.md. |
Architecture — Event Bus Map
LumisCare Event Bus — Current State Map
Date: 2026-04-04 Author: Martin Kleppmann (Distributed Systems Audit) Method: Static analysis of all publisher and listener classes in backend/services/ Source of truth: Actual Java source files, not design documents
Architecture
Azure Service Bus Premium tier, topics/subscriptions model. Each service owns its own topic and publishes events to it. Consumers subscribe to specific topics with SQL filters on event_type.
The design document (Service-Bus-Subscriptions-Design.md) specifies three topics: visit-events, schedule-events, notification-events. The actual implementation has diverged — publishers use five distinct topic names across six services, and the notification-service listener is wired to a different topic (domain-events) than what the visit and scheduling publishers write to.
Event Flow Diagram
graph LR
subgraph WIRED["FULLY WIRED"]
HR["hr-service<br/>(HrEventPublisher)"]
SCHED["scheduling-service<br/>(AppointmentEventPublisher)"]
SCHED_AVAIL["scheduling-service<br/>(HrAvailabilityUpdatedEventHandler)"]
NOTIF_PUB["notification-service<br/>(NotificationEventPublisher)"]
end
subgraph PARTIAL["PARTIALLY WIRED — publisher exists, consumer absent or mis-wired"]
VISITS["visits-service<br/>(VisitEventPublisher)"]
INCIDENTS["incidents-service<br/>(EventPublisher)"]
SAFETY["safety-service<br/>(SafetyEventPublisher)"]
NOTIF_LISTEN["notification-service<br/>(ServiceBusEventListener)"]
SCHED_CAREPLAN["scheduling-service<br/>(CarePlanPublishedEventHandler)"]
end
subgraph BROKEN["BROKEN — no publisher class exists"]
ASSESS["assessment-service"]
CAREPLAN["careplan-service"]
FINANCE["finance-service<br/>(OutboxEvent entity only)"]
end
HR -->|"employee.created/updated<br/>availability.updated<br/>leave.approved<br/>certification.expiring<br/>topic: domain-events"| SCHED_AVAIL
HR -->|"leave.approved<br/>topic: domain-events"| NOTIF_LISTEN
VISITS -->|"visit.started/completed/missed<br/>visit.task.completed<br/>topic: visits-events"| NOTIF_LISTEN
VISITS -->|"visit.completed<br/>topic: visits-events"| FINANCE_STUB["finance-service<br/>(NO listener class)"]
SCHED -->|"schedule.published/updated<br/>appointment.created/updated/cancelled<br/>topic: scheduling-events"| NOTIF_LISTEN
NOTIF_PUB -->|"notification.sent/failed/acknowledged<br/>topic: notification-events"| ANALYTICS_STUB["analytics-service<br/>(NOT IMPLEMENTED)"]
INCIDENTS -->|"incident.created<br/>topic: incidents-events"| NOTIF_LISTEN
SAFETY -->|"lwp.escalation.*<br/>topic: safety-events"| NOTIF_LISTEN
CAREPLAN_STUB["careplan-service<br/>(NO publisher)"] -.->|"careplan.published — MISSING"| SCHED_CAREPLAN
ASSESS -.->|"assessment.completed — MISSING"| CAREPLAN_STUB
style WIRED fill:#1a4731,color:#fff
style PARTIAL fill:#713f12,color:#fff
style BROKEN fill:#7f1d1d,color:#fff
Critical Architectural Finding: Topic Name Mismatch
The notification-service ServiceBusEventListener subscribes to topic domain-events (default configured at ServiceBusConfig.java:29):
azure.servicebus.topic-name:domain-events
The visits-service VisitEventPublisher publishes to topic visits-events (default configured at VisitEventPublisher.java:45):
azure.servicebus.visits-topic-name:visits-events
The scheduling-service AppointmentEventPublisher publishes to topic scheduling-events (default configured at AppointmentEventPublisher.java:45):
azure.servicebus.scheduling-topic-name:scheduling-events
The HR-service HrEventPublisher publishes to topic domain-events (default configured at HrEventPublisher.java:40):
azure.servicebus.topic-name:domain-events
This means: the notification-service listener will receive HR events but will silently miss all visit and schedule events at runtime unless the application properties are overridden in deployment configuration. There are no application.properties or application.yml files with explicit topic overrides found in the source tree — only Spring @Value defaults. This must be validated against the actual Azure Container Apps environment variables before treating any visit-to-notification flow as working.
Event-by-Event Status
| Event | Topic (default) | Producer Service | Publisher Class | Consumer Service | Listener Class | Status |
|---|---|---|---|---|---|---|
employee.created |
domain-events |
hr-service | HrEventPublisher |
scheduling-service | HrAvailabilityUpdatedEventHandler |
WIRED — both sides present |
employee.updated |
domain-events |
hr-service | HrEventPublisher |
scheduling-service | HrAvailabilityUpdatedEventHandler |
WIRED — both sides present |
availability.updated |
domain-events |
hr-service | HrEventPublisher (via AvailabilityPreferencesService) |
scheduling-service | HrAvailabilityUpdatedEventHandler (listens for hr.availability.updated) |
WIRED — subject used in code is hr.availability.updated, design doc says availability.updated — schema mismatch risk |
leave.approved |
domain-events |
hr-service | HrEventPublisher (via LeaveManagementService) |
notification-service | ServiceBusEventListener (case leave.approved) |
WIRED — both sides on same topic |
certification.expiring |
domain-events |
hr-service | HrEventPublisher (via CertificationExpiryScheduler) |
notification-service | ServiceBusEventListener (case certification.expiring) |
WIRED — both sides on same topic |
visit.started |
visits-events |
visits-service | VisitEventPublisher.publishVisitStarted() |
notification-service | ServiceBusEventListener (case visit.checkedin — event name mismatch) |
BROKEN — publisher emits visit.started, listener handles visit.checkedin; different names |
visit.completed |
visits-events |
visits-service | VisitEventPublisher.publishVisitCompleted() |
notification-service | ServiceBusEventListener (case visit.completed) |
TOPIC MISMATCH — publisher on visits-events, listener on domain-events |
visit.completed |
visits-events |
visits-service | VisitEventPublisher.publishVisitCompleted() |
finance-service | No listener class found | NOT WIRED — finance has no Service Bus consumer |
visit.missed |
visits-events |
visits-service | VisitEventPublisher.publishVisitMissed() |
notification-service | No explicit case in listener | NOT HANDLED — listener has no visit.missed case |
visit.task.completed |
visits-events |
visits-service | VisitEventPublisher.publishTaskCompleted() |
notification-service | No explicit case in listener | NOT HANDLED |
checkin.timeout |
visits-events |
visits-service | NOT FOUND in VisitEventPublisher methods |
notification-service | ServiceBusEventListener (case checkin.timeout) |
PUBLISHER ABSENT — listener expects this event but publisher has no publishCheckinTimeout() method |
assistance.requested |
visits-events |
visits-service | NOT FOUND in VisitEventPublisher methods |
notification-service | ServiceBusEventListener (case assistance.requested) |
PUBLISHER ABSENT — listener expects this event but publisher has no method |
task.flagged |
visits-events |
visits-service | NOT FOUND in VisitEventPublisher methods |
notification-service | ServiceBusEventListener (case task.flagged) |
PUBLISHER ABSENT — listener expects this but only visit.task.completed is published |
redflag.detected |
unknown | unknown | NOT FOUND | notification-service | ServiceBusEventListener (case redflag.detected) |
PUBLISHER ABSENT — no publisher produces this event |
visit.reassigned |
unknown | unknown | NOT FOUND | notification-service | ServiceBusEventListener (case visit.reassigned) |
PUBLISHER ABSENT |
schedule.published |
scheduling-events |
scheduling-service | AppointmentEventPublisher.publishSchedulePublished() |
notification-service | ServiceBusEventListener (case schedule.published) |
TOPIC MISMATCH — publisher on scheduling-events, listener on domain-events |
schedule.updated |
scheduling-events |
scheduling-service | AppointmentEventPublisher (no explicit publishScheduleUpdated found — has publishScheduleApproved) |
notification-service | ServiceBusEventListener (case not found) |
PARTIAL — method naming diverges from design doc |
careplan.published |
careplan-events |
careplan-service | NO PUBLISHER CLASS | scheduling-service | CarePlanPublishedEventHandler (subscribes to careplan-events) |
BROKEN — listener fully implemented, publisher does not exist |
careplan.updated |
unknown | careplan-service | NO PUBLISHER CLASS | notification-service | ServiceBusEventListener (case careplan.updated) |
BROKEN — no publisher |
assessment.completed |
unknown | assessment-service | NO PUBLISHER CLASS | careplan-service | NO LISTENER CLASS | NOT WIRED — entire chain absent |
incident.created |
incidents-events |
incidents-service | EventPublisher |
notification-service | ServiceBusEventListener — no incident.created case found in routing switch |
TOPIC MISMATCH + case absent — publisher on incidents-events, listener on domain-events |
lwp.escalation.level1/2/3/4 |
safety-events |
safety-service | SafetyEventPublisher.publishLWPEscalation() |
notification-service | ServiceBusEventListener (cases lwp.escalation.level1 through level4) |
TOPIC MISMATCH — publisher on safety-events, listener on domain-events |
invoice.created |
none | finance-service | NO PUBLISHER — only OutboxEvent entity + repository |
notification-service | ServiceBusEventListener (no case found) |
NOT WIRED — outbox pattern incomplete, no dispatcher daemon |
invoice.paid |
none | finance-service | NO PUBLISHER | analytics-service | NOT IMPLEMENTED | NOT WIRED |
timesheet.created |
none | finance-service | NO PUBLISHER — TimesheetBatchJobProcessor writes OutboxEvent but no Service Bus dispatch |
notification-service | ServiceBusEventListener (no case found) |
NOT WIRED — outbox records written to DB but no relay to Service Bus |
notification.sent |
notification-events |
notification-service | NotificationEventPublisher.publishNotificationSent() |
analytics-service | NOT IMPLEMENTED | PUBLISHER WIRED, consumer service does not exist |
notification.failed |
notification-events |
notification-service | NotificationEventPublisher.publishNotificationFailed() |
analytics-service | NOT IMPLEMENTED | PUBLISHER WIRED, consumer service does not exist |
daily.summary |
unknown | unknown | NOT FOUND | notification-service | ServiceBusEventListener (case daily.summary) |
PUBLISHER ABSENT |
Broken Chains
Chain 1: Assessment to Care Plan AI Generation (Clinical Safety Risk)
The primary clinical workflow trigger is broken at its first link.
assessment-servicehas no event publisher class anywhere in its source tree. When an assessment is completed, noassessment.completedevent is emitted.careplan-servicehas no event listener class. Even if an event were emitted, nothing would receive it.- Both sides of the Assessment → CarePlan trigger are absent.
Where it breaks: assessment-service/src/main/java/ — no *Publisher*.java file exists in this directory.
Chain 2: Care Plan to Scheduling (Scheduling Cannot Start Automatically)
The CarePlanPublishedEventHandler in scheduling-service is fully implemented (300+ lines, idempotency logic, recurring schedule creation). It is waiting for a careplan.published event on topic careplan-events. That event is never published.
careplan-servicehas no publisher class. The service can mark a care plan as published in the database (CarePlan.publishedAt,CarePlan.publishedByfields exist) but does not emit the event.
Where it breaks: careplan-service/src/main/java/ — no *Publisher*.java file exists. The handler waiting for the event is at scheduling-service/src/main/java/com/lumiscare/scheduling/event/CarePlanPublishedEventHandler.java:101.
Chain 3: Visit Check-in to Notification (Event Name Mismatch)
The design document specifies visit.checkedin. The VisitEventPublisher publishes visit.started (set at VisitEventPublisher.java:127). The ServiceBusEventListener routes on visit.checkedin (at ServiceBusEventListener.java:207). These names do not match. Even if the topic mismatch were fixed, this chain would still silently fail.
Where it breaks:
- Publisher sets name:
VisitEventPublisher.java:127—"visit.started" - Listener routes on:
ServiceBusEventListener.java:207—case "visit.checkedin"
Chain 4: Visit Completed to Finance (Finance Cannot Generate Invoice Line Items)
VisitEventPublisher.publishVisitCompleted() publishes to topic visits-events. The finance-service has no ServiceBusProcessorClient, no listener class, and no subscription configuration. The billing data embedded in the visit.completed payload (rates, task categories, mileage) is never consumed. Invoice line item generation is not automated.
Where it breaks: finance-service/src/main/java/ — no Service Bus consumer class of any kind. Finance only writes OutboxEvent records to its own database for the timesheet batch job, which itself has no Service Bus dispatcher.
Chain 5: Critical Safety Alerts Silently Dropped (Topic Mismatch)
SafetyEventPublisher publishes to topic safety-events. ServiceBusEventListener in notification-service subscribes to topic domain-events. The LWP escalation events (lwp.escalation.level1 through level4) will never reach the notification service unless an application property override is set in deployment. For lone worker protection, this is a CQC compliance risk.
Where it breaks: SafetyEventPublisher.java:39 (topic: safety-events) vs ServiceBusConfig.java:29 (subscribed to: domain-events).
Chain 6: Visit Events Unreachable by Notification Service (Topic Mismatch)
VisitEventPublisher publishes to visits-events. AppointmentEventPublisher publishes to scheduling-events. ServiceBusEventListener subscribes to domain-events. Unless deployment environment variables override these defaults, all visit and schedule events are invisible to the notification service. This silences checkin.timeout, task.flagged, assistance.requested, and schedule.published notifications.
Where it breaks: Topic default misalignment across three services — see Critical Architectural Finding section above.
Chain 7: Events Listened For But Never Published
The ServiceBusEventListener routing switch includes cases for events that have no publisher anywhere in the codebase:
| Event | Missing Publisher | Listener Location |
|---|---|---|
checkin.timeout |
No publisher method in VisitEventPublisher |
ServiceBusEventListener.java:207 |
assistance.requested |
No publisher method in VisitEventPublisher |
ServiceBusEventListener.java:211 |
redflag.detected |
No publisher anywhere in any service | ServiceBusEventListener.java:215 |
visit.reassigned |
No publisher anywhere in any service | ServiceBusEventListener.java:220 |
task.flagged |
No publisher method in VisitEventPublisher (publishes visit.task.completed instead) |
ServiceBusEventListener.java:224 |
daily.summary |
No publisher anywhere in any service | ServiceBusEventListener.java:249 |
Chain 8: Notification Outbound Events Have No Consumers
NotificationEventPublisher publishes notification.sent, notification.failed, and notification.acknowledged to topic notification-events. The analytics-service and audit-service that should consume these are not implemented as standalone services. The events will be published to a topic with no subscribers, age to TTL, and be discarded.
Fix Priority
Ordered by clinical and operational impact.
Priority 1 — Fix Topic Name Coherence (All Services)
Impact: Unblocks all visit and schedule notifications in one config change.
The notification-service ServiceBusConfig must subscribe to multiple topics, or all publishers must align on a shared topic name. The least-invasive fix is to add per-topic subscription processors in ServiceBusConfig.java for visits-events and scheduling-events in addition to domain-events. Alternatively, if the design intent is a single domain-events topic, all publishers must update their @Value defaults to domain-events.
This is a configuration and wiring change, not a logic change. No new business logic required.
Files to change:
notification-service/src/main/java/com/lumiscare/notification/config/ServiceBusConfig.java- OR all publisher
@Valuedefaults (4 publisher files)
Priority 2 — Fix visit.started / visit.checkedin Event Name
Impact: Allows check-in notifications to reach care managers.
Either rename the event type in VisitEventPublisher.java:127 from "visit.started" to "visit.checkedin", or update the listener case to "visit.started". The design document and listener both say visit.checkedin — the publisher is the outlier.
File to change: visits-service/src/main/java/com/lumiscare/visits/event/VisitEventPublisher.java:127
Priority 3 — Add Missing Publisher Methods to VisitEventPublisher
Impact: Enables checkin.timeout (clinical safety), task.flagged (care quality), assistance.requested (lone worker safety).
VisitEventPublisher needs three new publish methods. The listener routing and notification handlers for these events already exist. This is purely adding publisher-side methods and calling them from the appropriate visit execution service layer.
Events to add: checkin.timeout, task.flagged, assistance.requested
File to change: visits-service/src/main/java/com/lumiscare/visits/event/VisitEventPublisher.java
Priority 4 — Add careplan.published Publisher to careplan-service
Impact: Allows scheduling to automatically create recurring visits when a care plan is approved. This is the central automation trigger for care delivery.
The CarePlanPublishedEventHandler in scheduling-service is complete and production-ready. Only the publisher side is missing. Add a publisher class to careplan-service that emits careplan.published on topic careplan-events when a care plan transitions to published status (the state transition already exists in CarePlanMapper.java).
Files to create/change:
- Create:
careplan-service/src/main/java/com/lumiscare/event/CarePlanEventPublisher.java - Wire it into the publish endpoint in the careplan service controller
Priority 5 — Add assessment.completed Publisher to assessment-service
Impact: Triggers automatic AI care plan generation after assessment completion.
Create an event publisher in assessment-service and publish assessment.completed when an assessment reaches a completed status. The careplan-service needs a corresponding listener (this does not yet exist either — both sides must be built).
Files to create:
assessment-service/src/main/java/.../event/AssessmentEventPublisher.javacareplan-service/src/main/java/.../event/AssessmentCompletedEventListener.java
Priority 6 — Add Finance Service Bus Consumer for visit.completed
Impact: Automates invoice line item generation from completed visits.
Add a ServiceBusProcessorClient to finance-service that subscribes to the visits-events topic with filter event_type = 'visit.completed'. The billing data payload already contains all required fields (billing_data section with visit_type, care_types, day_type, travel_time_minutes, mileage_miles).
Files to create:
finance-service/src/main/java/.../event/VisitCompletedEventListener.java- Wire to existing rate calculation and invoice generation logic
Priority 7 — Complete the Finance Outbox Dispatcher
Impact: Enables timesheet.created and invoice.created notifications.
TimesheetBatchJobProcessor writes OutboxEvent records to the database but the comment in the code (TimesheetBatchJobProcessor.java:35) refers to an "external outbox-processor daemon" that does not exist. Either implement the outbox polling scheduler within the finance-service or replace the outbox pattern with a direct ServiceBusSenderClient call inside the existing transaction.
Files to change:
finance-service/src/main/java/com/lumiscare/icon/finance/service/TimesheetBatchJobProcessor.java- Optionally create a dedicated outbox publisher component
Priority 8 — Resolve hr.availability.updated vs availability.updated Event Name
Impact: Prevents silent message drops when carer availability changes.
HrAvailabilityUpdatedEventHandler expects message subject hr.availability.updated (at HrAvailabilityUpdatedEventHandler.java:94). HrEventPublisher publishes with event type availability.updated (called at AvailabilityPreferencesService.java:161). This is a subject field mismatch. The handler will silently complete all messages that do not match its expected subject, meaning availability changes will not trigger schedule recomputation.
Files to change: Align the event type string in AvailabilityPreferencesService.java:161 to hr.availability.updated, or update the handler constant at HrAvailabilityUpdatedEventHandler.java:94.
Summary Counts
| Category | Count |
|---|---|
| Fully wired event chains (publisher + consumer on matching topic) | 3 (employee.created, employee.updated, leave.approved/certification.expiring via domain-events) |
| Partially wired (publisher exists, consumer absent or topic mismatch) | 8 |
| Events listener handles but no publisher produces | 6 |
| Events completely absent (no publisher, no consumer) | 3 (assessment.completed, invoice.created, invoice.paid) |
| Publisher classes that exist | 6 (HrEventPublisher, VisitEventPublisher, AppointmentEventPublisher, NotificationEventPublisher, SafetyEventPublisher, incidents EventPublisher) |
| Publisher classes missing | 2 (assessment-service, careplan-service) |
| Services with no Service Bus integration at all | 3 (assessment-service, careplan-service, finance-service consumer side) |
| Topic name mismatches between publisher default and notification-service subscription | 3 (visits-events, scheduling-events, incidents-events, safety-events vs domain-events) |
The HR to Notification chain (leave.approved, certification.expiring) is the only end-to-end path that works without deployment configuration overrides. Everything that involves visit execution events, schedule events, or safety escalations depends on either a topic name fix or missing publisher code.
Architecture — Frontend Route Map
LumisCare Frontend — Complete Route Map
Generated: 2026-04-04
Source of truth: frontend/web/src/routes/index.ts + src/components/navigation/sidebar/Sidebar.tsx
Mobile source: mobile/app/src/app/ (Expo Router v4, file-based)
Total registered web routes: 97 (including aliases and redirects)
Portals covered: Back Office, Family Portal, Admin Panel, Mobile App
Back Office Routes
| Route | Component | Lines | Status | RBAC Guard | Notes |
|---|---|---|---|---|---|
/ (index) |
DashboardPage |
568 | Functional | All backoffice roles | Full metrics + rota section |
/clients |
ClientsPage |
131 | Functional | Most backoffice roles | Tabbed navigation, delegates to content components |
/clients/:id |
ClientPage |
— | Functional | Most backoffice roles | Client detail page |
/assessments |
AssessmentsPage |
— | Functional | Clinical + admin roles | Assessments overview |
/clients/:clientId/assessment/live |
LiveAssessmentPage |
— | Functional | None (no ProtectedRoute) | Live assessment flow with client context |
/clients/assessment/live |
LiveAssessmentPage |
— | Functional | None | Live assessment flow without client context |
/clients/assessment/transcript |
AssessmentTranscriptPage |
— | Functional | None | Assessment transcript view |
/clients/:clientId/body-map |
BodyMapPage |
— | Functional | None | Body observations with client context |
/clients/body-map |
BodyMapPage |
— | Functional | None | Body observations without client context |
/clients/:clientId/assessment/complete |
AssessmentCompletionPage |
— | Functional | None | Consent + attendees with client context |
/clients/assessment/complete |
AssessmentCompletionPage |
— | Functional | None | Consent + attendees without client context |
/clients/assessment/questions/:sectionId |
QuestionsPage |
— | Functional | None | Section questions view |
/clients/assessment/question-management |
QuestionManagementPage |
— | Functional | Clinical + admin roles | Drag-and-drop reorder |
/clients/:clientId/assessment/question-management |
QuestionManagementPage |
— | Functional | Clinical + admin roles | Drag-and-drop reorder with client context |
/clients/assessment/risk-assessment-risks |
RAQuestionsPage |
— | Functional | None | Risk assessment questions |
/clients/assessment/risk-assessment-risks/:sectionId |
RAQuestionsPage |
— | Functional | None | Risk assessment questions by section |
/clients/assessment/section/:sectionId |
PersonalInformationPage |
— | Functional | None | Section detail view |
/clients/assessment/medication/:medicationId/edit |
EditMedicationPage |
— | Functional | Clinical + admin roles | Edit medication (QA excluded) |
/clients/:clientId/assessment/medications/add |
AddMedicationPage |
— | Functional | Clinical + admin + carer | Add medication with client context |
/clients/assessment/medications/add |
AddMedicationPage |
— | Functional | Clinical + admin + carer | Add medication without client context |
/clients/:clientId/assessment/medications/:medicationId |
MedicationDetailsPage |
— | Functional | None | Medication details (read-only for QA) |
/clients/:clientId/assessment/medications/:medicationId/edit |
EditMedicationPage |
— | Functional | Clinical + admin roles | Edit medication with client context |
/employees |
EmployeePage |
753 | Functional | None (no ProtectedRoute) | Full employee list |
/employees/new |
CreateEmployeePage |
— | Functional | Manager roles only | Create employee (QA excluded) |
/employees/:id |
EmployeeDetailsPage |
— | Functional | None | Employee detail page |
/employees/find-by-skills |
FindCarersBySkillsPage |
— | Functional | None | Search carers by qualifications |
/leaves |
LeavesOverviewPage |
563 | Functional | None | Leave management overview |
/leaves/approval |
LeaveRequestApprovalPage |
— | Functional | Manager + shift-leader roles | Leave request approval (Feature #252) |
/hr/training-matrix |
TrainingMatrixPage |
636 | Functional (1 TODO) | HR + manager + admin roles | Training compliance matrix |
/hr/dbs |
DBSDashboardPage |
393 | Functional (1 TODO) | HR + manager + admin roles | DBS certificate status |
/hr/bradford |
BradfordDashboardPage |
372 | Functional (1 TODO) | HR + manager + admin roles | Bradford Factor scoring |
/hr/supervisions |
SupervisionSchedulePage |
487 | Functional (1 TODO) | HR + manager + admin roles | Supervision scheduling |
/hr/care-certificate |
CareCertificateTrackerPage |
450 | Functional (1 TODO) | HR + manager + admin roles | Care Certificate tracker |
/hr/cqc-pack |
CQCInspectionPackPage |
554 | Functional | HR + manager + admin roles | CQC inspection pack |
/visits |
VisitPage |
— | Functional | None | Visit list and management |
/visits/activity-log |
ActivityLogPage |
— | Functional | None | Visit activity log |
/visits/gps-checkin |
GPSCheckInSimulationPage |
548 | Functional | None | GPS geofence test tool (Feature #275) |
/visits/evv |
EVVPage |
111 | Functional | None | Electronic Visit Verification / QR check-in (Feature #277) |
/visits/live-tracking |
LiveTrackingDashboard |
— | Functional | None | Real-time carer location (Feature #287) |
/visits/completed-events |
CompletedEventsPage |
— | Functional | None | Completed visit review before invoicing |
/visits/handoff |
ShiftHandoffPage |
— | Functional | None | SBAR shift handoff notes |
/backoffice/scheduling |
SchedulingPage |
799 | Functional (1 TODO) | Coordinator + manager + admin | Full calendar/roster grid |
/backoffice/incidents (index) |
IncidentsListPage |
759 | Functional (1 TODO) | None | Incident list |
/backoffice/incidents/:id |
IncidentDetailPage |
2487 | Functional | None | Incident detail (investigation tab RBAC-gated inline) |
/backoffice/incidents/safeguarding |
SafeguardingPage |
59 | Functional | None | RBAC wrapper — delegates to ClientsSafeguardContent |
/care-plans |
CarePlansPage |
599 | Functional (1 TODO) | None | Care plans list |
/care-plans/:id |
CarePlanReviewPage |
864 | Functional | Manager + clinical + admin | Care plan approval workflow |
/care-plans/:id/edit |
CarePlanEditorPage |
1768 | Partial (53 TODOs) | Manager + clinical + admin | 10-section editor — incomplete fields |
/care-plans/:id/task-linking |
CarePlanTaskLinkingPage |
— | Functional | Manager + coordinator + clinical | Task-to-visit-type linking |
/care-plans/reviews-due |
CarePlansReviewDuePage |
480 | Functional | Manager + clinical + QA | NICE QS123 compliance reviews |
/ai/care-plan-generator |
AICarePlanGeneratorPage |
582 | Functional | Clinical + manager + admin | AI-powered care plan generation |
/finance |
FinancePage |
82 | Functional | ModuleProtectedRoute: finance | Tabbed finance hub |
/finance/audit-log |
InvoicesAuditLogPage |
— | Functional | ModuleProtectedRoute: finance | Invoice audit trail |
/finance/billing |
InsuranceBillingPage |
— | Functional | ModuleProtectedRoute: finance | Medicaid/Medicare claim management |
/billing |
BillingPage |
26 | Redirect | None | Redirects to /finance/billing |
/intake |
AddClientPage |
— | Functional | Clinical + manager (QA excluded) | New client intake |
/smart-upload |
SmartUploadPage |
— | Functional | Clinical + manager (QA excluded) | AI PDF document extraction |
/reports |
ReportsPage |
283 | Functional | None | Report generation UI |
/cqc-readiness |
CQCReadinessPage |
277 | Functional | Manager + clinical + QA + regional | KLOE evidence + compliance scoring |
/training/compliance |
TrainingCompliancePage |
306 | Functional | Manager + HR + QA + regional | Staff training gap analysis |
/profile |
ProfilePage |
— | Functional | None | User profile (all authenticated users) |
/notifications |
NotificationsInboxPage |
— | Functional | None | Notification feed (all authenticated users) |
/settings/notifications |
NotificationPreferencesPage |
— | Functional | None | Channel preferences (Feature #322) |
Back Office Alias / Redirect Routes
| Route | Destination | Type | Notes |
|---|---|---|---|
/dashboard |
/ |
Navigate redirect | Legacy path compatibility |
/compliance |
/admin/compliance |
Navigate redirect | BUG: wrong destination for backoffice users |
/incidents |
/backoffice/incidents |
Navigate redirect | Legacy path compatibility |
/schedule |
SchedulingPage (rendered directly) |
Alias | Renders same component as /backoffice/scheduling |
/training-compliance |
TrainingCompliancePage (rendered directly) |
Alias | Renders same component as /training/compliance |
/care-notes |
familyCareNotesPage (rendered directly) |
Alias | Family portal alias |
/care-plan |
familyCarePlanPage (rendered directly) |
Alias | Family portal alias |
/invoices |
familyInvoicesPage (rendered directly) |
Alias | Family portal alias |
Family Portal Routes
| Route | Component | Lines | Status | RBAC Guard | Notes |
|---|---|---|---|---|---|
/family-portal-dashboard |
familyDashboardPage |
522 | Functional (1 TODO) | family-member + admin roles | Family dashboard |
/family-portal-carenotes |
CareNotesPage |
1272 | Functional | family-member + admin roles | Full read view of visit notes |
/family-portal-careplan |
CarePlanPage |
797 | Functional (2 TODOs) | family-member + admin roles | Care plan view |
/family-portal-invoices |
InvoicesPage |
2479 | Functional (2 TODOs) | family-member + admin roles | Invoices and billing |
Alias routes (workaround patches):
| Alias Route | Resolves To | Notes |
|---|---|---|
/care-notes |
familyCareNotesPage |
Shortcut alias |
/care-plan |
familyCarePlanPage |
Shortcut alias |
/invoices |
familyInvoicesPage |
Shortcut alias |
Missing family portal features: No messaging/communication between family and carers. No visit scheduling from family side. No emergency contacts view.
Admin Panel Routes
Core Admin Routes
| Route | Component | Lines | Status | RBAC Guard | Notes |
|---|---|---|---|---|---|
/admin/organizations |
OrganizationsPage |
511 | Functional (5 TODOs) | system-admin + provider-admin | Organization management |
/admin/users |
UsersPage |
991 | Functional (7 TODOs) | system-admin only | User management |
/admin/roles |
RolesPage |
387 | Functional (1 TODO) | system-admin only | Roles and permissions |
/admin/settings |
SettingsPage |
524 | Functional | system-admin only | System settings |
/admin/audit-logs |
AuditLogsPage |
514 | Functional (1 TODO) | system-admin only | Audit trail |
/admin/compliance |
ComplianceDashboardPage |
555 | Functional | system-admin only | HIPAA compliance dashboard |
/admin/dscr |
DSCRCompliancePage |
386 | Functional | system-admin + provider-admin | NHS DSCR compliance |
/admin/data-import |
DataImportPage |
— | Functional | system-admin only | CSV/JSON data import |
/admin/data-export |
DataExportPage |
— | Functional | system-admin only | CSV/JSON data export |
/admin/multi-tenancy |
MultiTenancyTestPage |
— | Functional | system-admin only | Organization isolation test |
/admin/module-subscriptions |
ModuleSubscriptionsPage |
— | Functional | system-admin only | Per-org module access (Feature #360) |
/admin/questions |
QuestionManagementPage |
— | Functional | system-admin + provider-admin | Assessment question management (Feature #233) |
Admin Test / Diagnostic Pages
These 18 routes are permanent in the router but function as feature verification tools. They should be gated by a dev/debug feature flag in production builds.
| Route | Component | Feature # | RBAC Guard | Purpose |
|---|---|---|---|---|
/admin/encryption-compliance |
EncryptionCompliancePage |
#362 | system-admin | Encryption at rest verification |
/admin/tls-security |
TLSSecurityTestPage |
#363 | system-admin | TLS 1.3/1.2 enforcement |
/admin/data-residency |
DataResidencyPage |
#365 | system-admin | US data sovereignty |
/admin/openapi-validation |
OpenAPIValidationPage |
#366 | system-admin | API spec conformance |
/admin/token-enrichment |
TokenEnrichmentTestPage |
#16 | system-admin | JWT token enrichment |
/admin/api-auth-test |
ApiAuthTestPage |
#17 | system-admin | Bearer token validation |
/admin/api-versioning |
APIVersioningTestPage |
#367 | system-admin | /api/v1/ prefix verification |
/admin/api-sort-test |
APISortTestPage |
#369 | system-admin | Sort parameter verification |
/admin/request-validation |
RequestValidationTestPage |
#371 | system-admin | Input validation |
/admin/cors-test |
CORSTestPage |
#373 | system-admin | CORS header verification |
/admin/fk-constraints |
ForeignKeyConstraintsTestPage |
#374 | system-admin | Referential integrity |
/admin/check-constraints |
CheckConstraintsTestPage |
#376 | system-admin | Data validation constraints |
/admin/push-notification-test |
PushNotificationTestPage |
#317 | system-admin + provider-admin | Azure Notification Hub push |
/admin/sms-notification-test |
SmsNotificationTestPage |
#319 | system-admin + provider-admin | Azure Communication Services SMS |
/admin/quiet-hours-test |
QuietHoursTestPage |
#321 | system-admin + provider-admin | Critical notification bypass |
/admin/channel-routing-test |
ChannelRoutingTestPage |
#323 | system-admin + provider-admin | Channel preference routing |
/admin/device-registration-test |
DeviceRegistrationTestPage |
#325 | system-admin + provider-admin | Mobile device token storage |
/admin/notification-template-test |
NotificationTemplateTestPage |
#324 | system-admin + provider-admin | Template variable substitution |
/admin/delivery-tracking-test |
DeliveryTrackingTestPage |
#327 | system-admin + provider-admin | Notification delivery logging |
/admin/notification-retry-test |
NotificationRetryTestPage |
#328 | system-admin + provider-admin | Failed notification retry |
/admin/in-app-read-tracking-test |
InAppReadTrackingTestPage |
#329 | system-admin + provider-admin | Read status tracking |
/admin/notification-analytics |
NotificationAnalyticsPage |
#329 | system-admin + provider-admin | Delivery statistics dashboard |
Admin Sidebar Navigation (from Sidebar.tsx)
| Sidebar Label | Route | Allowed Roles |
|---|---|---|
| Organizations | /admin/organizations |
system-admin + provider-admin |
| Users | /admin/users |
system-admin + provider-admin |
| Roles & Permissions | /admin/roles |
system-admin only |
| Settings | /admin/settings |
system-admin + provider-admin |
| Audit Logs | /admin/audit-logs |
system-admin only |
| HIPAA Compliance | /admin/compliance |
system-admin only |
| NHS DSCR | /admin/dscr |
system-admin + provider-admin |
Mobile Screens (Expo Router v4)
File-based routing at mobile/app/src/app/. Routes correspond to the filesystem path under (app)/.
| Screen / File | Lines | Status | Notes |
|---|---|---|---|
(app)/index.tsx — Home/Schedule |
1624 | Functional | Large comprehensive carer dashboard |
(app)/schedule.tsx |
671 | Functional | Carer schedule view |
(app)/visit/[visitId]/index.tsx |
490 | Functional | Visit detail hub |
(app)/visit/[visitId]/care-note.tsx |
46 | Functional | Thin wrapper, delegates to CareNoteForm |
(app)/visit/[visitId]/emar.tsx |
1182 | Functional (2 TODOs) | Electronic MAR administration |
(app)/visit/[visitId]/vitals.tsx |
1087 | Needs review (19 TODOs) | Critical: clinically sensitive vitals screen — highest TODO density in codebase |
(app)/visit/[visitId]/medications.tsx |
122 | Functional | Medication list for visit |
(app)/visit/[visitId]/task-validation.tsx |
295 | Functional (1 TODO) | Task validation flow |
(app)/visit/[visitId]/signature.tsx |
308 | Functional (2 TODOs) | Signature capture |
(app)/visit/[visitId]/check-out.tsx |
5 | Functional | Thin delegator to CheckOutView |
(app)/visit/[visitId]/client-details.tsx |
264 | Functional | Client info within visit |
(app)/visit/[visitId]/structured-note.tsx |
— | Present | Structured clinical note entry |
(app)/visit/[visitId]/observation.tsx |
— | Present | Clinical observations |
(app)/visit/[visitId]/pictures.tsx |
— | Present | Camera/media capture |
(app)/visit/[visitId]/online-assistance.tsx |
— | Present | Remote assistance feature |
(app)/visit/[visitId]/manual-checkout.tsx |
— | Present | Manual override checkout |
(app)/visit/[visitId]/upload-progress.tsx |
— | Present | Upload status indicator |
(app)/assessment/index.tsx |
684 | Functional (1 TODO) | Mobile assessment flow |
(app)/profile/index.tsx |
490 | Functional | User profile |
(app)/profile/availability.tsx |
— | Present | Carer availability management |
(app)/video-call/[callId]/index.tsx |
31 | Present | LiveKit WebRTC video call |
(app)/voice-commands.tsx |
390 | Functional | Voice input feature |
(app)/notifications/index.tsx |
— | Present | Notification feed |
(app)/notifications/settings.tsx |
— | Present | Notification preferences |
(app)/settings.tsx |
— | Present | App settings |
(app)/dev-tools.tsx |
— | Present | Developer tools screen |
auth.tsx |
— | Present | OAuth callback handler |
Navigation Gaps
| Menu Item | Sidebar Path | Route Exists? | Content Status | Fix Needed |
|---|---|---|---|---|
| Dashboard | / |
Yes | Functional (568 lines) | None |
| Notifications | /notifications |
Yes | Functional | None |
| Clients | /clients |
Yes | Functional | None |
| Employees > DBS Status | /hr/dbs |
Yes | Functional (1 TODO) | None |
| Employees > Training Matrix | /hr/training-matrix |
Yes | Functional (1 TODO) | None |
| Employees > Bradford Factor | /hr/bradford |
Yes | Functional (1 TODO) | None |
| Employees > Supervisions | /hr/supervisions |
Yes | Functional (1 TODO) | None |
| Employees > Care Certificate | /hr/care-certificate |
Yes | Functional (1 TODO) | None |
| Employees > CQC Pack | /hr/cqc-pack |
Yes | Functional | None |
| Schedule (parent) | /visits |
Yes | Functional | Structural: parent links to /visits but sub-items link to /backoffice/scheduling, /visits/completed-events, /visits/handoff — mixed path hierarchy |
| Schedule > Rostering | /backoffice/scheduling |
Yes | Functional (1 TODO) | None |
| Schedule > Completed Events | /visits/completed-events |
Yes | Functional | None |
| Schedule > Shift Handoff | /visits/handoff |
Yes | Functional | None |
| Care Plans | /care-plans |
Yes | Functional (1 TODO) | None |
| AI Care Plan | /ai/care-plan-generator |
Yes | Functional | None |
| Assessments | /assessments |
Yes | Functional | None |
| Finance | /finance |
Yes | Functional (ModuleProtectedRoute) | None |
| Incidents | /backoffice/incidents/safeguarding |
Yes | Functional (thin wrapper) | Architectural: sidebar links to /safeguarding but incidents list is at /backoffice/incidents — users cannot reach incidents list from sidebar |
| Reports | /reports |
Yes | Functional | None |
| Compliance (parent) | /compliance |
Redirect only | Redirects to /admin/compliance |
BUG P1: backoffice sidebar Compliance parent path redirects to HIPAA admin panel, not to backoffice CQC/compliance hub |
| Compliance > CQC Readiness | /cqc-readiness |
Yes | Functional | None |
| Compliance > Training Compliance | /training/compliance |
Yes | Functional | None |
Mock Data Pages
Based on audit evidence, the following pages carry TODO markers explicitly referencing "connect to real API" or similar — indicating they may be rendering mock/sample data in some sections:
| Page | File Location | TODO Count | Nature of Mock |
|---|---|---|---|
CarePlanEditorPage |
features/backOffice/carePlans |
53 | Many section fields not wired to real API — // TODO: connect to real API patterns throughout |
vitals.tsx (mobile) |
mobile/app/src/app/(app)/visit/[visitId]/vitals.tsx |
19 | Clinically sensitive screen with incomplete API wiring |
UsersPage |
features/admin/users |
7 | Minor TODOs — some user management operations may fall back |
OrganizationsPage |
features/admin/organizations |
5 | Minor TODOs |
IncidentsListPage |
features/backOffice/incidents/list |
1 | Single TODO |
SchedulingPage |
features/backOffice/scheduling |
1 | Single TODO in 799-line page |
CarePlansPage |
features/backOffice/carePlans |
1 | Single TODO |
DBSDashboardPage |
features/backOffice/hr/pages/DBSDashboardPage |
1 | Single TODO |
TrainingMatrixPage |
features/backOffice/hr/pages/TrainingMatrixPage |
1 | Single TODO |
BradfordDashboardPage |
features/backOffice/hr/pages/BradfordDashboardPage |
1 | Single TODO |
SupervisionSchedulePage |
features/backOffice/hr/pages/SupervisionSchedulePage |
1 | Single TODO |
CareCertificateTrackerPage |
features/backOffice/hr/pages/CareCertificateTrackerPage |
1 | Single TODO |
familyPortalDashboardPage |
pages/familyPortal/dashboard |
1 | Minor TODO |
CarePlanPage (family) |
features/familyPortal/carePlan |
2 | Minor TODOs |
InvoicesPage (family) |
features/familyPortal/invoices |
2 | Minor TODOs |
AuditLogsPage |
features/admin/auditLogs |
1 | Minor TODO |
RolesPage |
features/admin/roles |
1 | Minor TODO |
Note on methodology: TODO counts use literal string grep of "TODO". They include code comments such as // TODO: connect to real API and represent technical debt markers, not blank or placeholder UI. No page renders "Coming soon" or empty content.
Stub / Placeholder Pages
No pages are pure stubs rendering blank or "Coming soon" content. All registered pages render substantive UI. The following are below 50 lines but delegate intentionally:
| Page | File | Lines | Nature |
|---|---|---|---|
BillingPage |
features/backOffice/billing |
26 | Intentional redirect wrapper to /finance/billing |
SafeguardingPage |
features/backOffice/incidents/safeguarding |
59 | Intentional RBAC wrapper delegating to ClientsSafeguardContent |
check-out.tsx (mobile) |
(app)/visit/[visitId]/check-out.tsx |
5 | Intentional thin delegator to CheckOutView component |
care-note.tsx (mobile) |
(app)/visit/[visitId]/care-note.tsx |
46 | Intentional thin wrapper for CareNoteForm |
video-call/[callId]/index.tsx (mobile) |
(app)/video-call/[callId]/index.tsx |
31 | LiveKit integration, thin entry point |
Unrouted Components
The following components exist in the source tree but have no registered route in src/routes/index.ts:
| Component | File Path | Concern Level | Notes |
|---|---|---|---|
SOSHistoryPage |
features/backOffice/dashboard/SOSHistoryPage.tsx (96 lines) |
P1 | Referenced in comment as /backoffice/safety/sos-history but route never registered — feature dead end |
KeywordPage |
features/backOffice/keywords/pages/KeywordPage.tsx |
P1 | Keyword management feature exists with no route — unreachable |
RedFlagDetectionDemoPage |
features/backOffice/clients/pages/RedFlagDetectionDemoPage.tsx |
P2 | Demo/diagnostic page exists but is unrouted |
Summary Statistics
| Metric | Count |
|---|---|
| Total registered web routes (including aliases/redirects) | ~97 |
| Back Office routes (primary) | 56 |
| Back Office alias/redirect routes | 8 |
| Family Portal routes (primary) | 4 |
| Family Portal alias routes | 3 |
| Admin core routes | 12 |
| Admin test/diagnostic routes | 22 |
Auth routes (/login, /logout, /access-denied) |
3 |
| Catch-all 404 route | 1 |
| Mobile screens | 27 |
| Pages with TODO markers (web) | 17 |
| Unrouted components | 3 |
| Navigation gaps (bugs) | 2 confirmed |
Priority Issues
| Priority | Issue | Impact |
|---|---|---|
| P0 | CarePlanEditorPage (1768 lines, 53 TODOs) — core clinical page, incomplete fields |
Clinical product gap |
| P0 | Mobile vitals.tsx (1087 lines, 19 TODOs) — clinically sensitive screen |
Clinical product gap |
| P1 | Sidebar /compliance redirects to HIPAA admin page instead of backoffice CQC/compliance hub |
Wrong UX for all backoffice users |
| P1 | Sidebar Incidents links to /backoffice/incidents/safeguarding — users cannot reach the incidents list from the sidebar |
Navigation dead end |
| P1 | SOSHistoryPage component exists (96 lines) but has no registered route |
Feature dead end |
| P1 | KeywordPage component exists but is not routed |
Unreachable feature |
| P2 | Family portal URLs (/family-portal-dashboard etc.) violate URL hierarchy — alias patches mask the structural problem |
UX and URL semantics debt |
| P2 | RedFlagDetectionDemoPage exists but is unrouted |
Dead code |
| P2 | switch.tsx file and switch/ folder coexist in src/components/ui/ — import resolution ambiguity |
Build risk |
| P2 | calender folder typo in src/components/ui/ |
DX friction, propagates through all calendar imports |
| P3 | 22 admin test/diagnostic pages as permanent routes — should be gated by dev/debug feature flag | Security surface area in production, route tree clutter |
| P3 | SafeguardingPage (59 lines) delegates to ClientsSafeguardContent — component lives in Clients feature, not Incidents |
Architectural boundary violation |
Fix Backlog — P0/P1/P2 Issues
LumisCare — Fix Backlog
Consolidated from 5 expert reports — 2026-03-25 Total issues: 32
Format: [PORTAL] [PAGE] [ELEMENT] [PROBLEM] [FIX NEEDED]
P0 — Demo Blocking
Issues that will be visible within 60 seconds of any stakeholder opening the app. Must be fixed before any demo or investor showing.
| # | Portal | Page | Element | Problem | Fix Needed |
|---|---|---|---|---|---|
| 1 | Admin Panel | Organizations (/admin/organizations) |
"Add Organization" button | Button renders; no form or modal wired | Wire button to create organization form/modal with: Organization Name, Status, Contact details → POST /api/v1/organizations |
| 2 | Admin Panel | Organizations (/admin/organizations) |
Per-card "More options" (…) button | Button renders; no dropdown or menu attached | Add dropdown menu with actions (Edit, View, Deactivate) per org card |
| 3 | Family Portal | Care Plan (/family-portal-careplan) |
Print button (header) | onClick handler missing — button does nothing |
Wire to window.print() or PDF export |
| 4 | Family Portal | Care Plan (/family-portal-careplan) |
More options (3-dot) button (header) | onClick handler missing — button does nothing |
Add dropdown with relevant actions (Download, Share) or remove button |
| 5 | Family Portal | Care Notes (/family-portal-carenotes) |
"Week" button (rounded-full with left chevron) | No onClick handler — looks interactive but does nothing |
Wire to goToPreviousWeek() (same as the arrow button) or replace with correct UI |
| 6 | Family Portal | Care Plan (/family-portal-careplan) |
Draw Signature area inside Sign & Consent Modal | Draw tab shows placeholder <div>, no canvas or drawing library hooked up |
Integrate a canvas drawing library (e.g. react-signature-canvas) — draw mode must capture a real signature |
| 7 | Back Office | Body Map (/clients/:id/body-map) |
"This Week" time filter button | No onClick handler wired |
Wire to filter observations by current week, or remove button until implemented |
| 8 | Back Office | Body Map (/clients/:id/body-map) |
"Filters" button | No onClick handler wired |
Open filter drawer/modal for observation category/severity, or remove button until implemented |
| 9 | Back Office | Body Map (/clients/:id/body-map) |
Zoom controls (minus / zoom icon / plus) | UI renders; no zoom logic implemented | Implement zoom state and apply CSS transform on body diagram container |
| 10 | Back Office | Body Map (/clients/:id/body-map) |
Per observation card "Edit" (pencil icon) | onEdit(id) handler marked TODO — not implemented |
Implement edit observation flow (open edit dialog pre-populated with observation data) |
| 11 | Family Portal | Invoices (/family-portal-invoices) |
Per-row "Pay" button | Calls console.log or shows "Online payment will be available in the next release." toast; no payment flow |
For demo: show a modal stub. For production: integrate payment provider. Disable with clear "Coming Soon" state if not ready. |
| 12 | Family Portal | Dashboard (/family-portal-dashboard) |
Per-invoice "Pay" button | Same as above — triggers info toast only | Same fix as above |
| 13 | Back Office | Visits Header (/visits) |
Print button | console.log placeholder — "Print functionality coming soon" |
Wire to print or remove button |
| 14 | Back Office | Visits Header (/visits) |
Export button | console.log placeholder — "Export functionality coming soon" |
Wire to CSV/PDF export or remove button |
| 15 | Back Office | Activity Log (/visits/activity-log) |
Print button | No onClick handler wired |
Wire to print or remove button |
| 16 | Back Office | Activity Log (/visits/activity-log) |
Export button | No onClick handler wired |
Wire to export (CSV at minimum) or remove button |
| 17 | Family Portal | Dashboard | Video thumbnails in visit cards | Play icon overlay shown on thumbnails but no onClick handler — non-interactive |
Either remove play overlay or implement video playback modal |
P1 — Pilot Blocking
Issues that block a paying pilot customer from using core workflows. Must be resolved before any live pilot.
| # | Portal | Page | Element | Problem | Fix Needed |
|---|---|---|---|---|---|
| 18 | Admin Panel | Audit Logs (/admin/audit-logs) |
Export functionality | No export button present anywhere on the page | Add export button → generate CSV/PDF of audit log entries with current filters applied |
| 19 | Admin Panel | Audit Logs (/admin/audit-logs) |
Date range filter | No date range picker present — only type filter and text search | Add date range picker (start/end) to filter audit log by date |
| 20 | Admin Panel | Compliance Dashboard (/admin/compliance) |
All action items | Page is fully read-only — data rights requests, anomaly alerts, consent override all require action | Add action controls: respond to data rights requests, acknowledge anomaly alerts, manage consent |
| 21 | Family Portal | Profile (/profile) |
Edit contact information | Profile page is fully read-only — no edit buttons, no form, no save | Add edit form for at minimum: phone number, preferred contact method. Name/email via Azure AD. |
| 22 | Admin Panel | Organizations (/admin/organizations) |
Organization data | All data is hardcoded array in component state — not API-connected | Connect to /api/v1/organizations GET (list) and POST (create) endpoints |
| 23 | Admin Panel | Roles & Permissions (/admin/roles) |
Role management | Roles page is view-only — no "Add Role" or "Edit Role" UI | Add ability to create custom roles and edit permissions, or document that this is intentionally RBAC-fixed |
| 24 | Back Office | Activity Log (/visits/activity-log) |
"This Month" date filter button | Button renders with dropdown chevron but no onClick handler wired |
Wire to date range picker — currently filters are impossible to apply by date on this page |
| 25 | Back Office | Completed Events Tab (/visits — Completed Events) |
Date Range button | Visual button with no date picker wired | Wire to date range picker for filtering completed events by date range |
| 26 | Back Office | Finance | Billing Page | Entire page is placeholder: <div>Welcome to BillingPage</div> |
Implement billing page or redirect to Insurance Billing page until ready |
| 27 | Back Office | Scheduling | Recompute Schedule | Demo-only — hardcoded with carer-1 / "Sarah Mitchell" data |
Connect to real scheduling data and real recomputation logic |
P2 — Production Blocking
Issues that are acceptable for demo/pilot but must be resolved before production launch. Real persistence and real backend required.
| # | Portal | Page | Element | Problem | Fix Needed |
|---|---|---|---|---|---|
| 28 | Admin Panel | Settings (all tabs) | Save Changes | Persists to localStorage only |
Connect to backend settings API with proper per-org persistence |
| 29 | Admin Panel | Assessment Questions | Save Question | Persists to localStorage('admin_assessment_questions') |
Connect to backend questions API |
| 30 | Admin Panel | Profile | Save Photo | Stores base64 in localStorage |
Upload to blob storage; store reference in user record |
| 31 | Back Office | Body Map | Add Observation | Saves to localStorage('lumiscare-body-map-observations-{clientId}') |
Persist observations to assessment service / clinical records |
| 32 | Back Office | Care Plan Review | Schedule Review | Saves to localStorage('lumiscare_scheduled_care_plan_reviews') and localStorage('lumiscare_schedule_blocks') |
Persist to scheduling service; create real calendar entries |
Summary by Portal
| Portal | P0 | P1 | P2 | Total |
|---|---|---|---|---|
| Back Office (Clinical/Scheduling/Finance/HR) | 8 | 4 | 3 | 15 |
| Admin Panel | 2 | 6 | 3 | 11 |
| Family Portal | 7 | 1 | 0 | 8 |
| Total | 17 | 11 | 6 | 32 |
Quick Wins (P0 items fixable in < 1 hour each)
These P0 bugs are simple missing handler wires — not missing features:
- [P0-3] Family Portal Care Plan Print button —
onClick={() => window.print()} - [P0-4] Family Portal Care Plan More options button — add dropdown or remove icon
- [P0-5] Family Portal Care Notes "Week" button —
onClick={goToPreviousWeek} - [P0-13] Backoffice Visits Print button —
onClick={() => window.print()} - [P0-15] Activity Log Print button —
onClick={() => window.print()} - [P0-7] Body Map "This Week" button — remove or stub with toast "Filters coming soon"
- [P0-8] Body Map "Filters" button — remove or stub with toast "Filters coming soon"
- [P0-17] Dashboard video thumbnails — remove play icon overlay until implemented
Generated 2026-03-25. Source: consolidated from 5 expert subagent reports.
Mock-to-Real Migration Plan
Mock API to Real Backend — Migration Plan
Generated: 2026-03-26 Author: FlowForge (ALAI DevOps) Project: LumisCare Enterprise Healthcare Platform Scope: 222 mock endpoints → 11 Spring Boot microservices via Web BFF
1. Current State Summary
| Component | Status |
|---|---|
| Mock API (server.js) | Running — 5,280 lines, 222 endpoints, frontend pointed here |
| Spring Boot services | 11 deployed on Azure Container Apps — all Running |
| PostgreSQL (10 databases) | Flyway migrated, schemas applied, zero seed data |
| Web BFF | Deployed — assessment, careplan, incidents, hr, finance clients wired; visits + scheduling + notification + identity NOT fully wired |
| Frontend | Points to mock API |
2. Coverage Analysis — 222 Mock Endpoints vs Real Services
Method
Mock endpoints extracted from mock-api/server.js via route registration.
Real coverage determined from OpenAPI specs in openapi-specs/ and Web BFF controller inventory in backend/bff/web-bff/src/main/java/.../controller/.
Total endpoint count breakdown:
- 222 total mock registrations
- 18 are catch-all wildcards (
/api/*,/employees/:id, etc.) — not real routes - 204 are distinct addressable mock endpoints
Coverage By Service Domain
VISITS — 14 mock endpoints
Mock routes: GET/POST /api/v1/visits, GET/PATCH/DELETE /api/v1/visits/:id, check-in, check-out, eMAR, tasks, clinical-observations, SOS, travel-record, publish, running-late, pre-visit-brief
Real service (visits-service): OpenAPI defines /visits, /visits/{visitId}, check-in, check-out, tasks, observations, status, notes, eMAR.
Web BFF: No VisitsController found in BFF controller list. VISITS_SERVICE_BASE_URL is configured in application.yml (port 8085) but no BFF controller proxies it yet.
Coverage status: Service READY, BFF wiring MISSING. Estimated real coverage: 9 of 14 endpoints have a backend implementation. 5 (clinical-observations format, SOS, pre-visit-brief, travel-record, eMAR administer/refuse detail) need verification.
SCHEDULING / ROSTERING — 8 mock endpoints
Mock routes: GET /api/v1/schedule-blocks, GET/POST/PATCH/DELETE /api/v1/schedule-blocks/:id, GET /api/v1/completed-events, bulk-confirm, publish
Real service (scheduling-service): OpenAPI defines schedule-events, appointments, rostered-hours, recurring-schedules, availability. Maps partially — schedule-blocks naming differs from schedule-events.
Web BFF: CalendarController.java exists. SCHEDULING_SERVICE_BASE_URL configured (port 8086).
Coverage status: PARTIAL — naming mismatch between mock and real service paths needs alignment. Estimated real coverage: 5 of 8.
ASSESSMENT — 12 mock endpoints
Mock routes: GET/POST/PUT/PATCH /api/v1/assessments, body-map, scores, complete, GET /api/v1/intake
Real service (assessment-service): Full OpenAPI — assessment CRUD, sections, subsections, questions, risk sections, observations, photos, medications, status, submit, intake.
Web BFF: BffAssessmentController.java + AssessmentWebDashBoardController.java exist. AssessmentClientConfig wired.
Coverage status: COVERED for core paths. Scores/clinical-tools endpoints may map to risk-section in real service. Estimated real coverage: 10 of 12.
CARE PLANS — 14 mock endpoints
Mock routes: GET/POST/PUT/DELETE /api/v1/care-plans, versions, tasks, risk-alerts, reviews, approve, publish, submit-for-approval, consent, review-due
Real service (careplan-service): OpenAPI defines care-plans CRUD, publish, regenerate, generation-status, versions, master-task-types/visit-types.
Web BFF: CarePlanController.java exists. CarePlanClientConfig wired.
Coverage status: Core CRUD + publish + versions covered. Task management, risk-alerts, reviews, consent are mock-only. Estimated real coverage: 7 of 14.
HR — 18 mock endpoints
Mock routes: GET /api/v1/hr/supervisions, absences, dbs, bradford, care-certificate, training matrix, supervision-schedule, cqc-inspection-pack; legacy /employees/*
Real service (hr-service): Extensive OpenAPI — employees full profile, leaves, supervisions, training, appraisals, documents, equality diversity, onboarding.
Web BFF: EmployeeController.java + ResumeUploadController.java exist. HrClientConfig wired.
Coverage status: Employee core COVERED. HR compliance sub-paths (DBS checks, Bradford factor, care certificate, CQC pack) are mock-only abstractions — hr-service has training and supervision but not as named sub-routes. Estimated real coverage: 10 of 18.
FINANCE — 20 mock endpoints
Mock routes: invoices, timesheets, rate-cards, funders, invoice-groups, payment-groups, payroll preview/export/approve, aged-debtors, NMW compliance, bank-holidays, month-end status, dashboard
Real service (finance-service): OpenAPI defines invoices (full lifecycle), timesheets, invoice-groups, payment-groups, mandates, dashboard. Rate-cards and funders are mock-only concepts.
Web BFF: FinanceBffController.java exists. FinanceClientConfig wired.
Coverage status: Invoice + timesheet core COVERED. Rate-cards, funders, payroll export, NMW compliance, aged-debtors are MOCK-ONLY. Estimated real coverage: 12 of 20.
INCIDENTS — 8 mock endpoints
Mock routes: GET/POST/PATCH/DELETE /api/v1/incidents, GET /api/v1/incidents/:id
Real service (incidents-service): OpenAPI defines accidents, complaints, hazards, safeguarding, summary, actions log.
Web BFF: IncidentsController.java exists. IncidentsClientConfig wired.
Coverage status: COVERED — mock uses generic /incidents but real service has typed categories (accidents, complaints, hazards, safeguarding). Path mapping required in BFF.
Estimated real coverage: 6 of 8.
NOTIFICATIONS — 10 mock endpoints
Mock routes: notifications inbox, unread-count, preferences, devices register/unregister, mark-all-read, push
Real service (notification-service): OpenAPI defines inbox, devices, preferences, templates, unread-count, send, mark-read.
Web BFF: No NotificationController found. notification-service NOT listed in application.yml service URLs.
Coverage status: Service READY, BFF wiring MISSING. Estimated real coverage: 7 of 10 (paths align), but BFF layer absent.
IDENTITY / USERS / ORGS / SERVICE USERS — 14 mock endpoints
Mock routes: GET /api/v1/users, GET /api/v1/service-users, organizations, settings, carers, skills
Real service (identity-service): OpenAPI defines users, organizations, service-users, roles, settings.
Web BFF: ServiceUsersController.java exists. IdentityClientConfig found in config.
Coverage status: PARTIAL — users and service-users COVERED. Carers-by-skills, skills list, rostering-settings are mock-only. Estimated real coverage: 9 of 14.
FAMILY PORTAL — 16 mock endpoints
Mock routes: family care-plan view, complaints, messages/threads, notifications, payments initiate/confirm
Real service: No dedicated family-portal-service. Family portal routes would be served by web-bff aggregating careplan-service + notification-service + identity-service.
Web BFF: No family portal controller found.
Coverage status: MOCK-ONLY. No real backend for family portal BFF layer. Estimated real coverage: 0 of 16.
MISC (dashboard, CQC, EVV, import, audit, observations, insurance, handoff) — 24 mock endpoints
Includes: GET /api/v1/dashboard/stats, /api/v1/cqc-readiness, /api/v1/evv/*, /api/v1/import/*, /api/v1/audit-logs, /api/v1/insurance/*, /api/v1/handoff/*, /api/v1/observations
Real services: EVV (electronic visit verification) maps to visits-service tracking endpoints. Dashboard stats aggregate from multiple services. Import, insurance, handoff are mock-only constructs.
Coverage status: MOSTLY MOCK-ONLY. Estimated real coverage: 4 of 24 (dashboard + some audit, EVV via visits tracking).
Coverage Summary Table
| Domain | Mock Endpoints | Real Coverage | Status |
|---|---|---|---|
| Visits | 14 | ~9 | BFF wiring missing |
| Scheduling | 8 | ~5 | Path naming mismatch |
| Assessment | 12 | ~10 | BFF wired, good coverage |
| Care Plans | 14 | ~7 | BFF wired, tasks/risks mock-only |
| HR | 18 | ~10 | BFF wired, compliance routes mock-only |
| Finance | 20 | ~12 | BFF wired, rate-cards/payroll mock-only |
| Incidents | 8 | ~6 | BFF wired, path mapping needed |
| Notifications | 10 | ~7 | BFF wiring MISSING |
| Identity/Users/Orgs | 14 | ~9 | BFF partial |
| Family Portal | 16 | 0 | No backend at all |
| Misc / EVV / Dashboard | 24 | ~4 | Mostly mock-only |
| TOTAL | 158 (excl wildcards) | ~79 | ~50% real coverage |
Bottom line: approximately 79 of 158 meaningful mock endpoints have a corresponding real service implementation. The remaining ~79 are either BFF-wiring gaps, path-mapping gaps, or features that do not yet exist in any real service.
3. Seed Data Blocking Issue
Problem
Azure PostgreSQL Flexible Server blocks all public inbound connections by default. The 10 service databases have schemas (Flyway applied) but no seed data. Attempts to run seed scripts from a local machine are blocked by the firewall.
Options to Load Seed Data
Option A — Azure Cloud Shell (Recommended, zero setup)
# Open Azure Portal → Cloud Shell (Bash)
# Download seed scripts from repo or paste inline
psql "host=<your-pg-server>.postgres.database.azure.com \
user=<admin> \
dbname=identity_db \
sslmode=require" \
-f seed-identity.sql
No firewall changes needed. Cloud Shell is inside Azure's network boundary.
Option B — Temporary Firewall Rule (simple but requires cleanup)
# Add current IP to Azure PostgreSQL firewall
MY_IP=$(curl -s https://api.ipify.org)
az postgres flexible-server firewall-rule create \
--resource-group rg-vcc-dev-001 \
--name <pg-server-name> \
--rule-name temp-local-dev \
--start-ip-address $MY_IP \
--end-ip-address $MY_IP
# Run seeds, then REMOVE the rule
az postgres flexible-server firewall-rule delete \
--resource-group rg-vcc-dev-001 \
--name <pg-server-name> \
--rule-name temp-local-dev --yes
Option C — Run psql from a Container App (uses private endpoint, no firewall change)
# Exec into any running Container App that has psql available
az containerapp exec \
--name ca-vcc-dev-identity-api-001 \
--resource-group rg-vcc-dev-001 \
--command "/bin/bash"
# Inside the container:
psql $DATABASE_URL -f /tmp/seed.sql
Option D — Seed via Service REST API (safest, uses existing auth/validation)
Write seed data through the service's own API endpoints (POST requests). Slower but validates business logic along with data. Best for production-like seed data.
Recommended approach for dev environment: Option A (Azure Cloud Shell) for bulk seed load, then Option D for per-feature test data.
4. Migration Strategy
Recommended: Option C — Proxy Pattern in Web BFF
The Web BFF already exists and has partial service wiring. The safest migration path is:
- Web BFF handles ALL frontend requests
- For endpoints with real service backing: BFF proxies to the real microservice
- For endpoints without real backing: BFF falls back to mock API (temporarily)
- Mock API is never called directly by frontend — only via BFF fallback
This is superior to Option A (hard cutover) and Option B (per-service switch) because:
- Frontend changes exactly once: point to BFF URL
- Rollback is instant (mock fallback still running)
- Each endpoint can be promoted independently with zero frontend changes
- Canary testing per-route is trivial
Migration Architecture
Frontend (React)
|
v
Web BFF (Azure Container Apps, port 8080)
|-- /api/v1/assessments --> assessment-service (LIVE)
|-- /api/v1/care-plans --> careplan-service (LIVE)
|-- /api/v1/incidents/* --> incidents-service (LIVE)
|-- /api/v1/hr/* --> hr-service (LIVE)
|-- /api/v1/finance/* --> finance-service (LIVE)
|-- /api/v1/visits --> visits-service (WIRE NOW)
|-- /api/v1/notifications/* --> notification-service (WIRE NOW)
|-- /api/v1/schedule-blocks/* --> scheduling-service (WIRE NOW)
|-- /api/v1/family/* --> [MOCK FALLBACK] no service yet
|-- /api/v1/evv/* --> [MOCK FALLBACK] no service yet
|-- /api/v1/dashboard/stats --> [BFF aggregation from multiple services]
|-- /api/v1/cqc-readiness --> [MOCK FALLBACK]
|-- everything else --> [MOCK FALLBACK]
|
v (fallback only)
Mock API (Node.js, to be decommissioned incrementally)
5. Step-by-Step Migration Plan
Phase 0 — Preparation (1-2 days)
0.1 Load seed data
- Use Azure Cloud Shell + Option A above
- Minimum seed sets needed per service:
identity_db: 1 organization, 3-5 users (admin, care-manager, carer), 2-3 service usersassessment_db: 2-3 assessment templates (assessment_questions, assessment_sections)careplan_db: master task types, master visit typesvisits_db: visit statuses, task typeshr_db: leave types, training categoriesscheduling_db: appointment typesfinance_db: invoice statuses, timesheet statusesincidents_db: incident categories (accidents, complaints, hazards, safeguarding)notification_db: notification templates
0.2 Verify all 11 Container Apps are healthy
az containerapp list \
--resource-group rg-vcc-dev-001 \
--query "[].{name:name, status:properties.runningStatus}" \
--output table
0.3 Set frontend env var (do not deploy yet)
# In frontend/web/.env.production
VITE_API_BASE_URL=https://<web-bff-fqdn>.azurecontainerapps.io
Phase 1 — Wire Missing BFF Routes (3-5 days)
1.1 Wire visits-service to BFF
- Add
VisitsController.javatobackend/bff/web-bff/src/main/java/.../controller/ - Configure
VISITS_SERVICE_BASE_URLenv var in Container App (already in application.yml) - Map mock routes to real service paths:
GET /api/v1/visits→GET /visits(visits-service)POST /api/v1/visits/:id/check-in→POST /visits/{visitId}/check-inPOST /api/v1/visits/:id/check-out→POST /visits/{visitId}/check-outGET /api/v1/visits/:id/emar→GET /visits/{visitId}/emar
1.2 Wire notification-service to BFF
- Add
NotificationController.java - Add
notification.service.base-urltoapplication.yml - Set
NOTIFICATION_SERVICE_BASE_URLenv var in Container App - Map:
/api/v1/notifications/*→ notification-service paths
1.3 Wire scheduling-service to BFF
CalendarController.javaexists — extend to handleschedule-blocksaliasschedule-blocksin mock =schedule-eventsin real service- Map:
GET/POST /api/v1/schedule-blocks→GET/POST /schedule-events
1.4 Verify identity-service routing
ServiceUsersController.javaexists — confirm it handles/api/v1/service-users/*- Add routing for
/api/v1/organizations/*and/api/v1/users/*
Phase 2 — Switch Frontend to BFF (1 day)
2.1 Update frontend environment
# In frontend/web/ Azure Static Web App config:
VITE_API_BASE_URL=https://<web-bff-fqdn>.azurecontainerapps.io
2.2 Add BFF fallback proxy for unimplemented routes
In Web BFF, add a fallback route that proxies unknown /api/v1/* requests to the mock API:
# application-dev.yml
mock-api:
fallback-url: ${MOCK_API_URL:http://mock-api:3000}
fallback-enabled: ${MOCK_API_FALLBACK_ENABLED:true}
This ensures zero frontend breakage during migration. As each BFF controller is added, the fallback for that route is removed.
2.3 Keep mock API running (do not shut down) Mock API remains as a fallback target for the BFF. It can be stopped when fallback-enabled is set to false.
Phase 3 — Per-Domain Cutover (1-2 weeks)
Work through each domain in priority order. For each:
- Confirm seed data in that service's DB
- Smoke-test the real service endpoint directly (health + basic CRUD)
- Enable BFF routing to real service
- Disable fallback for that domain's routes
- Run Playwright smoke tests
Priority order:
- Identity (login, users, orgs) — everything else depends on auth
- Assessment (core clinical workflow)
- Care Plans (built on assessment)
- Visits (mobile carer workflow)
- Incidents (safety-critical, short paths)
- HR (staff management)
- Scheduling (complex, validate separately)
- Finance (invoicing, last due to rate-card gaps)
- Notifications (async, lower urgency)
- Family Portal (no service — keep mock until service is built)
Phase 4 — Decommission Mock (after Phase 3 complete)
4.1 Disable BFF fallback
MOCK_API_FALLBACK_ENABLED=false
4.2 Run full Playwright test suite Confirm zero regressions. Mock fallback should not be needed.
4.3 Stop mock API Container App (or LaunchAgent if local)
az containerapp update \
--name ca-vcc-dev-mock-api \
--resource-group rg-vcc-dev-001 \
--min-replicas 0 \
--max-replicas 0
4.4 Archive mock-api/server.js (do not delete — reference for missing features)
6. Rollback Plan
Rollback is available at every phase with zero data loss risk.
Instant rollback (any phase)
# Re-point frontend to mock API
# In Azure Static Web App config:
VITE_API_BASE_URL=https://mock-api-url
# OR in BFF — re-enable fallback
MOCK_API_FALLBACK_ENABLED=true
Because the mock API is never decommissioned during active migration (only at Phase 4), rollback is a single env var change at the frontend or BFF level.
Per-service rollback
If a specific service is misbehaving, disable only its BFF route and re-enable fallback for that domain only. All other services continue serving real data.
Database rollback
Schema rollback is possible via Flyway undo migrations. Data rollback requires a database restore from Azure Backup (automated daily snapshots on PostgreSQL Flexible Server).
7. Endpoints That Are Mock-Only (No Real Service Equivalent)
These require new service implementation or design decisions before migration is possible:
| Mock Endpoint | Category | Effort |
|---|---|---|
GET /api/v1/finance/rate-cards |
Finance | Medium — add to finance-service |
GET /api/v1/finance/funders |
Finance | Medium — add to finance-service |
GET /api/v1/finance/payroll/* |
Finance | High — payroll is a separate domain |
GET /api/v1/finance/aged-debtors |
Finance | Low — calculated report from invoices |
GET /api/v1/finance/nmw-compliance |
Finance | Medium |
GET /api/v1/hr/bradford |
HR | Low — calculated from absences |
GET /api/v1/hr/care-certificate |
HR | Medium |
GET /api/v1/hr/cqc-inspection-pack |
HR | High |
GET /api/v1/cqc-readiness |
Compliance | Medium — aggregate |
GET /api/v1/evv/* |
EVV | Medium — visits-service has tracking |
GET /api/v1/family/* |
Family Portal | High — no BFF layer exists |
GET /api/v1/insurance/* |
Insurance | Not in architecture — park for now |
GET /api/v1/import/* |
Bulk Import | Medium — batch job, not REST |
GET /api/v1/handoff/* |
Handoff notes | Low — maps to visits-service notes |
GET /api/v1/observations |
Observations | Low — maps to assessment-service |
8. Timeline Estimate
| Phase | Work | Duration |
|---|---|---|
| Phase 0 — Seed data + health verification | 1-2 days | DevOps + 1 backend dev |
| Phase 1 — Wire 3 missing BFF routes | 3-5 days | 2 backend devs |
| Phase 2 — Frontend switch + BFF fallback | 1 day | 1 frontend dev + 1 backend dev |
| Phase 3 — Per-domain cutover (10 domains) | 8-10 days | 2 backend devs |
| Phase 4 — Decommission mock | 1 day | DevOps |
| Total | ~3 weeks |
9. Risk Register
| Risk | Likelihood | Impact | Mitigation |
|---|---|---|---|
| Real service returns different schema than mock | High | High | Adapter layer in BFF; validate against OpenAPI spec |
| Empty seed data causes null pointer errors | Medium | Medium | Populate seed before Phase 2 cutover |
| PostgreSQL connection limits under load | Low | High | Connection pooling via PgBouncer (already in infra) |
| Visits-service geofence check fails on dev | Medium | Medium | Disable geofence enforcement flag in dev profile |
| Auth token propagation missing in new BFF controllers | Medium | High | Follow existing WebClientConfig JWT propagation pattern |
| Family portal with no service breaks the portal | High | Medium | Mock fallback covers family routes until service is built |
| Azure Container App cold start latency | Low | Low | Min replicas = 1 already set in dev.bicepparam |
10. Key File Locations
| File | Purpose |
|---|---|
/Users/makinja/projects/client/lumiscare/mock-api/server.js |
Source of truth for all mock endpoint behavior |
/Users/makinja/projects/client/lumiscare/backend/bff/web-bff/src/main/resources/application.yml |
BFF service URL configuration |
/Users/makinja/projects/client/lumiscare/backend/bff/web-bff/src/main/java/.../controller/ |
BFF controllers — add new ones here |
/Users/makinja/projects/client/lumiscare/backend/bff/web-bff/src/main/java/.../config/ |
Service client configs — add wiring here |
/Users/makinja/projects/client/lumiscare/openapi-specs/ |
OpenAPI specs per service — source of truth for real paths |
/Users/makinja/projects/client/lumiscare/infrastructure/env-parameters/dev.bicepparam |
Azure infra sizing and image names |
DA Corrections — 9 Critical Gaps Addressed
Added 2026-03-26 — Devils Advocate review identified these gaps in the original plan. All 9 are corrections to existing sections or new sections not previously covered.
DA-1. Endpoint Priority Matrix
Full classification of all 222 mock endpoint registrations (18 catch-all wildcards excluded = 204 addressable endpoints).
Status definitions:
REAL_READY— Spring Boot service has the endpoint AND BFF wiring exists. Can migrate immediately after seed data is loaded.NEEDS_WIRING— Spring Boot service endpoint exists in OpenAPI spec, but BFF controller for this route is absent or incomplete.MOCK_ONLY— No real service endpoint. Feature does not exist in any deployed Spring Boot service.SCOPE_OUT— Not needed for MVP. Defer to post-MVP roadmap.
| Domain | Mock Route | Status | Notes |
|---|---|---|---|
| Assessment | GET /api/v1/assessments |
REAL_READY | BFF wired |
GET /api/v1/assessments/:id |
REAL_READY | BFF wired | |
POST /api/v1/assessments |
REAL_READY | BFF wired | |
PUT /api/v1/assessments/:id |
REAL_READY | BFF wired | |
PATCH /api/v1/assessments/:id |
REAL_READY | BFF wired | |
POST /api/v1/assessments/:id/complete |
REAL_READY | BFF wired | |
GET /api/v1/assessments/:id/body-map |
NEEDS_WIRING | Service has photos/observations endpoints; body-map is a frontend-assembled view | |
PUT /api/v1/assessments/:id/body-map |
NEEDS_WIRING | As above | |
GET /api/v1/assessments/:id/scores |
NEEDS_WIRING | Maps to risk-section in assessment-service | |
GET /api/v1/assessments/:id/scores/:tool |
NEEDS_WIRING | As above | |
POST /api/v1/assessments/:id/scores/:tool |
NEEDS_WIRING | As above | |
GET /api/v1/intake |
REAL_READY | OpenAPI /api/v1/intake defined |
|
GET /api/v1/intake/:id |
REAL_READY | OpenAPI /api/v1/intake/{intake_id} defined |
|
POST /api/v1/intake |
REAL_READY | OpenAPI defined | |
PATCH /api/v1/intake/:id |
NEEDS_WIRING | OpenAPI has GET/POST only; PATCH not in spec | |
DELETE /api/v1/intake/:id |
SCOPE_OUT | Not in spec; soft-delete not required for MVP | |
| Care Plans | GET /api/v1/care-plans |
REAL_READY | BFF wired |
GET /api/v1/care-plans/:id |
REAL_READY | BFF wired | |
POST /api/v1/care-plans |
REAL_READY | BFF wired | |
PUT /api/v1/care-plans/:id |
REAL_READY | BFF wired | |
DELETE /api/v1/care-plans/:id |
REAL_READY | BFF wired | |
POST /api/v1/care-plans/:id/publish |
REAL_READY | BFF wired | |
GET /api/v1/care-plans/:id/versions |
REAL_READY | BFF wired | |
GET /api/v1/care-plans/review-due |
MOCK_ONLY | No real endpoint; aggregate query needed | |
GET /api/v1/master-task-types |
REAL_READY | Service defines master-task-types | |
GET /api/v1/master-visit-types |
REAL_READY | Service defines master-visit-types | |
| Care plan tasks routes | MOCK_ONLY | careplan-service has task types, not task CRUD per plan | |
| Care plan risk-alerts routes | MOCK_ONLY | Not in any service spec | |
| Care plan reviews/consent routes | MOCK_ONLY | Not in any service spec | |
POST /api/v1/care-plans/:id/submit-for-approval |
MOCK_ONLY | Status lifecycle managed differently in real service | |
| Visits | GET /api/v1/visits |
NEEDS_WIRING | visits-service READY, no BFF VisitsController |
GET /api/v1/visits/:id |
NEEDS_WIRING | As above | |
POST /api/v1/visits |
NEEDS_WIRING | As above | |
PATCH /api/v1/visits/:id |
NEEDS_WIRING | As above | |
DELETE /api/v1/visits/:id |
NEEDS_WIRING | As above | |
POST /api/v1/visits/publish |
MOCK_ONLY | No equivalent in visits-service spec | |
POST /api/v1/visits/:id/check-in |
NEEDS_WIRING | visits-service has /visits/{visitId}/check-in |
|
POST /api/v1/visits/:id/check-out |
NEEDS_WIRING | visits-service has /visits/{visitId}/check-out |
|
GET /api/v1/visits/:id/emar |
NEEDS_WIRING | visits-service has /visits/{visitId}/emar |
|
POST /api/v1/visits/:id/emar |
NEEDS_WIRING | visits-service has /visits/{visitId}/emar POST |
|
| Clinical observations (per-visit) | NEEDS_WIRING | visits-service has /visits/{visitId}/observations |
|
| SOS / running-late / pre-visit-brief | MOCK_ONLY | Not defined in visits-service OpenAPI | |
| Travel record | MOCK_ONLY | Tracking endpoints exist but different shape | |
| Scheduling | GET /api/v1/schedule-blocks |
NEEDS_WIRING | scheduling-service has /schedule-events; CalendarController exists but path mismatch |
POST /api/v1/schedule-blocks |
NEEDS_WIRING | As above | |
PATCH /api/v1/schedule-blocks/:id |
NEEDS_WIRING | As above | |
DELETE /api/v1/schedule-blocks/:id |
NEEDS_WIRING | As above | |
GET /api/v1/completed-events |
MOCK_ONLY | No direct real equivalent; map to completed appointments | |
POST /api/v1/completed-events/:id/confirm |
MOCK_ONLY | scheduling-service manages this via status update | |
POST /api/v1/completed-events/bulk-confirm |
MOCK_ONLY | No bulk confirm endpoint in spec | |
| Schedule publish route | MOCK_ONLY | scheduling-service has /schedules/generate + /schedules/{id}/approve which differs |
|
| HR | GET /employees/list |
REAL_READY | BFF EmployeeController wired |
GET /employees/:id |
REAL_READY | BFF wired | |
POST /employees |
REAL_READY | BFF wired | |
PATCH /employees/:id |
REAL_READY | BFF wired | |
DELETE /employees/:id |
REAL_READY | BFF wired | |
GET /api/v1/leaves |
REAL_READY | hr-service has leaves endpoint | |
POST /api/v1/leaves |
REAL_READY | hr-service has leaves POST | |
PATCH /api/v1/leaves/:id |
REAL_READY | hr-service has leaves PATCH | |
DELETE /api/v1/leaves/:id |
REAL_READY | hr-service has leaves DELETE | |
GET /api/v1/hr/supervisions |
NEEDS_WIRING | hr-service has supervisions; BFF sub-path mapping needed | |
GET /api/v1/hr/absences |
NEEDS_WIRING | hr-service has absences/leaves | |
GET /api/v1/hr/dbs |
MOCK_ONLY | Not in hr-service OpenAPI; compliance documents endpoint covers this partially | |
GET /api/v1/hr/bradford |
MOCK_ONLY | Calculated from absences; no real endpoint | |
GET /api/v1/hr/care-certificate |
MOCK_ONLY | Not in hr-service spec | |
GET /api/v1/training/courses |
NEEDS_WIRING | hr-service has training; BFF not wired | |
GET /api/v1/training/compliance |
NEEDS_WIRING | hr-service has training compliance; BFF not wired | |
GET /api/v1/hr/cqc-inspection-pack |
MOCK_ONLY | No real equivalent — aggregate report | |
| Finance | GET /api/v1/finance/invoices |
REAL_READY | BFF FinanceBffController wired |
POST /api/v1/finance/invoices |
REAL_READY | BFF wired | |
PUT /api/v1/finance/invoices/:id |
REAL_READY | BFF wired | |
GET /api/v1/finance/invoice-groups |
REAL_READY | BFF wired | |
GET /api/v1/finance/payment-groups |
REAL_READY | BFF wired | |
GET /api/v1/finance/dashboard |
REAL_READY | BFF wired | |
| Finance timesheets routes | REAL_READY | finance-service defines timesheets | |
GET /api/v1/finance/rate-cards |
MOCK_ONLY | Not in finance-service spec | |
GET /api/v1/finance/funders |
MOCK_ONLY | Not in finance-service spec | |
GET /api/v1/finance/payroll/* |
MOCK_ONLY | Payroll is a separate domain | |
GET /api/v1/finance/aged-debtors |
MOCK_ONLY | Calculated report — not in spec | |
GET /api/v1/finance/nmw-compliance |
MOCK_ONLY | Not in finance-service spec | |
| Incidents | GET /api/v1/incidents |
NEEDS_WIRING | incidents-service uses typed routes (/accidents, /safeguarding, /complaints, /hazards); BFF IncidentsController must aggregate or map |
GET /api/v1/incidents/:id |
NEEDS_WIRING | Type-specific in real service | |
POST /api/v1/incidents |
NEEDS_WIRING | Type must be specified to route to correct sub-endpoint | |
PATCH /api/v1/incidents/:id |
NEEDS_WIRING | As above | |
DELETE /api/v1/incidents/:id |
NEEDS_WIRING | As above | |
GET /api/v1/incidents/summary |
REAL_READY | incidents-service has /summary |
|
| Export endpoints | REAL_READY | incidents-service has CSV export per type | |
| Notifications | GET /api/v1/notifications/inbox |
NEEDS_WIRING | notification-service READY, BFF NOT wired |
GET /api/v1/notifications/inbox/unread-count |
NEEDS_WIRING | As above | |
PATCH /api/v1/notifications/inbox/:id/read |
NEEDS_WIRING | As above | |
PATCH /api/v1/notifications/inbox/read-all |
NEEDS_WIRING | As above | |
POST /api/v1/notifications/mark-all-read |
NEEDS_WIRING | As above | |
GET /api/v1/notifications/preferences |
NEEDS_WIRING | notification-service has preferences | |
POST /api/v1/notifications/preferences |
NEEDS_WIRING | As above | |
PATCH /api/v1/notifications/preferences/quiet-hours |
NEEDS_WIRING | As above | |
| Notification device register/unregister | NEEDS_WIRING | notification-service has devices endpoint | |
GET /api/v1/notifications/analytics |
MOCK_ONLY | Not in notification-service spec | |
| Identity | GET /api/v1/service-users |
REAL_READY | identity-service + BFF ServiceUsersController |
GET /api/v1/service-users/:id |
REAL_READY | BFF wired | |
POST /api/v1/service-users |
REAL_READY | BFF wired | |
PATCH /api/v1/service-users/:id |
REAL_READY | BFF wired | |
DELETE /api/v1/service-users/:id |
REAL_READY | BFF wired | |
GET /api/v1/organizations |
NEEDS_WIRING | identity-service has organizations; BFF routing partial | |
GET /api/v1/organizations/:id |
NEEDS_WIRING | As above | |
GET /api/v1/users |
NEEDS_WIRING | identity-service has users; BFF routing partial | |
GET /users/about-me |
REAL_READY | identity-service /users/me |
|
GET /api/v1/carers |
NEEDS_WIRING | identity-service users filtered by carer role | |
POST /api/v1/carers/by-skills |
MOCK_ONLY | No real endpoint — would require identity + hr query | |
GET /api/v1/skills |
MOCK_ONLY | Not in any service spec | |
GET /api/v1/settings |
NEEDS_WIRING | identity-service has org settings | |
PATCH /api/v1/settings |
NEEDS_WIRING | As above | |
GET /api/v1/rostering-settings |
MOCK_ONLY | Not in any service spec | |
| Family Portal | All 16 GET/POST /api/v1/family/* routes |
MOCK_ONLY | No FamilyPortalService; no BFF controller (see DA-8) |
| Misc / EVV / Dashboard | GET /api/v1/dashboard/stats |
NEEDS_WIRING | Requires BFF aggregation across 4+ services |
GET /api/v1/cqc-readiness |
MOCK_ONLY | Aggregate report — no real service | |
GET /api/v1/evv/compliance |
NEEDS_WIRING | visits-service tracking endpoints cover EVV data | |
GET /api/v1/evv/records |
NEEDS_WIRING | visits-service has location tracking history | |
GET /api/v1/audit-logs |
MOCK_ONLY | No audit-service in architecture | |
GET /api/v1/insurance/* |
SCOPE_OUT | Not in product architecture — park post-MVP | |
GET /api/v1/import/* |
SCOPE_OUT | Batch job domain, not REST for MVP | |
GET /api/v1/handoff/notes |
NEEDS_WIRING | visits-service /visits/{visitId}/notes covers this |
|
GET /api/v1/handoff/history |
NEEDS_WIRING | visits-service notes with type=handover | |
GET /api/v1/supervisor/dashboard |
MOCK_ONLY | BFF aggregation — not yet designed | |
GET /api/v1/supervisor/dashboard/carer-locations |
NEEDS_WIRING | visits-service /tracking/locations/current |
|
GET /api/v1/observations (global) |
NEEDS_WIRING | visits-service has per-visit observations; no global endpoint |
Summary counts:
| Status | Count | Action |
|---|---|---|
| REAL_READY | ~65 | Wire frontend → BFF, validate schema (see DA-3) |
| NEEDS_WIRING | ~72 | Add BFF controller code (Phase 1 work) |
| MOCK_ONLY | ~52 | Keep on mock fallback; schedule for post-MVP |
| SCOPE_OUT | ~15 | Remove from BFF routing; return 404 or 501 |
DA-2. Seed Data Verification (Phase 0.4)
The original plan (Phase 0.1) lists what to seed but provides no way to verify seeds actually loaded. Add Phase 0.4 after all seed scripts have run.
Phase 0.4 — Verify Seed Data Loaded Correctly
Run these queries via Azure Cloud Shell after Phase 0.1 seed load:
-- identity_db
SELECT COUNT(*) AS orgs FROM organizations; -- expect >= 1
SELECT COUNT(*) AS users FROM users; -- expect >= 3
SELECT COUNT(*) AS service_users FROM service_users; -- expect >= 2
SELECT COUNT(*) AS roles FROM roles; -- expect >= 12 (system roles)
-- assessment_db
SELECT COUNT(*) AS intake FROM assessment_intake; -- expect >= 0 (can be empty)
SELECT COUNT(*) AS sections FROM assessment_sections; -- expect >= 7
-- careplan_db
SELECT COUNT(*) AS task_types FROM master_task_types; -- expect >= 5
SELECT COUNT(*) AS visit_types FROM master_visit_types; -- expect >= 3
-- visits_db
SELECT COUNT(*) AS visit_statuses FROM visit_status_lookup; -- expect >= 6 (or check enum table)
-- hr_db
SELECT COUNT(*) AS leave_types FROM leave_types; -- expect >= 4
SELECT COUNT(*) AS training_cats FROM training_categories; -- expect >= 3
-- scheduling_db
SELECT COUNT(*) AS appt_types FROM appointment_types; -- expect >= 4
-- finance_db
SELECT COUNT(*) AS inv_statuses FROM invoice_status_lookup; -- expect >= 4
-- incidents_db
SELECT COUNT(*) AS categories FROM incident_categories; -- expect >= 4 (acc, saf, cmp, haz)
-- notification_db
SELECT COUNT(*) AS templates FROM notification_templates; -- expect >= 5
verify-seeds.sh — script to run from Azure Cloud Shell:
#!/bin/bash
# verify-seeds.sh — Run from Azure Cloud Shell after Phase 0.1 seed load
# Usage: bash verify-seeds.sh <pg-server-name> <admin-user>
PG_SERVER="${1}.postgres.database.azure.com"
PG_USER="$2"
FAILED=0
run_check() {
local DB=$1 QUERY=$2 LABEL=$3 MIN=$4
COUNT=$(psql "host=$PG_SERVER user=$PG_USER dbname=$DB sslmode=require" \
-tAc "$QUERY" 2>/dev/null)
if [ -z "$COUNT" ] || [ "$COUNT" -lt "$MIN" ]; then
echo "FAIL [$DB] $LABEL: got $COUNT (expected >= $MIN)"
FAILED=$((FAILED+1))
else
echo "PASS [$DB] $LABEL: $COUNT rows"
fi
}
run_check "identity_db" "SELECT COUNT(*) FROM organizations;" "organizations" 1
run_check "identity_db" "SELECT COUNT(*) FROM users;" "users" 3
run_check "identity_db" "SELECT COUNT(*) FROM service_users;" "service_users" 2
run_check "identity_db" "SELECT COUNT(*) FROM roles;" "roles" 12
run_check "careplan_db" "SELECT COUNT(*) FROM master_task_types;" "master_task_types" 5
run_check "careplan_db" "SELECT COUNT(*) FROM master_visit_types;" "master_visit_types" 3
run_check "hr_db" "SELECT COUNT(*) FROM leave_types;" "leave_types" 4
run_check "scheduling_db" "SELECT COUNT(*) FROM appointment_types;" "appointment_types" 4
run_check "incidents_db" "SELECT COUNT(*) FROM incident_categories;" "incident_categories" 4
run_check "notification_db" "SELECT COUNT(*) FROM notification_templates;" "notification_templates" 5
if [ "$FAILED" -gt 0 ]; then
echo ""
echo "SEED VERIFICATION: $FAILED check(s) FAILED. Do NOT proceed to Phase 1."
exit 1
else
echo ""
echo "SEED VERIFICATION: All checks PASSED. Safe to proceed to Phase 1."
exit 0
fi
Gate: Phase 1 (BFF wiring) must not begin until verify-seeds.sh exits 0.
DA-3. Shape Mismatch Matrix
Comparison of mock API response shapes vs real OpenAPI-defined shapes for high-traffic routes. These mismatches require BFF adapter code — the BFF cannot simply proxy; it must transform.
Visits — GET /api/v1/visits
| Field | Mock shape | Real service shape | Action |
|---|---|---|---|
| Response wrapper | Array [...] |
Paginated object { content: [...], pageInfo: { ... } } |
BFF must extract content array or frontend must handle pagination |
| ID format | "visit-0001" (string with prefix) |
UUID "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" |
BFF must map or real service auto-generates UUIDs |
| Client reference | clientId + clientName (flat) |
serviceUserId (UUID) — no embedded name |
BFF must enrich with identity-service lookup or frontend fetches separately |
| Status values | "in-progress", "scheduled", "completed" (kebab-case) |
in_progress, scheduled, completed (snake_case enum) |
BFF adapter or frontend normalizes |
| Visit type | "personal-care", "medication" (kebab-case strings) |
AppointmentType enum: care_visit, assessment_visit, medication_only, welfare_check |
Mapping required — not 1:1 |
| GPS check-in | gpsCheckIn: { lat, lng } |
checkInLocation: { latitude, longitude, accuracy, altitude, timestamp } (GpsLocation schema) |
Field rename + structure change |
| Duration | duration: 60 (minutes, integer) |
Derived from scheduledStartTime / scheduledEndTime — no standalone duration field |
BFF calculates or omits |
| Required headers | None | X-VCU-Organization-Id, X-VCU-User-Id, X-VCU-Roles must be forwarded |
BFF must propagate JWT-extracted headers |
Incidents — GET /api/v1/incidents
| Field | Mock shape | Real service shape | Action |
|---|---|---|---|
| Response wrapper | { incidents: [...], total: N } |
Each type has own paginated endpoint: /accidents, /safeguarding, /complaints, /hazards |
BFF must fan-out to 4 endpoints and merge, OR frontend switches to type-specific routes |
id format |
"inc-001" |
UUID | BFF-generated or real service UUID |
type values |
"Accident", "Medication_Error", "Safeguarding" (mixed case, non-typed) |
Routes are typed: /accidents = type ACCIDENT/INCIDENT/NEAR_MISS; /safeguarding uses ConcernType enum |
Routing logic required in BFF |
status values |
"Resolved", "Under_Review", "Investigating" |
LogStatus enum: IDENTIFIED, RAISED, IN_PROGRESS, UPHELD, NOT_UPHELD, PARTIALLY_UPHELD, WITHDRAWN, RESOLVED |
Mapping table needed |
incident_number |
"INC-2024-001" (mock format) |
Auto-generated: "ACC-2026-00001", "SAF-2026-00001" etc. |
Display only; reference format changes |
| Duplicate fields | Both serviceUserName and service_user_name exist |
Single serviceUserId (UUID reference) |
Remove duplicates; BFF enriches name |
actionsTaken |
Embedded array of action objects | Separate endpoint: /logs/{logType}/{logId}/actions |
BFF must fetch separately or omit on list |
| Header | X-Org-Id (mock uses no header) |
X-VCC-Organization-Id required |
BFF must add |
Scheduling — GET /api/v1/schedule-blocks
| Field | Mock shape | Real service shape | Action |
|---|---|---|---|
| Path | /api/v1/schedule-blocks |
/api/v1/scheduling/schedule-events |
BFF maps path |
| Response wrapper | Array [...] |
Paginated { content: [...], ... } |
BFF unwraps or frontend adapts |
type values |
"team-meeting", "training", "supervision", "appraisal" |
ScheduleEventType enum: appraisal, meeting, supervision, training |
team-meeting → meeting mapping |
id format |
"sb-001" |
UUID | BFF maps |
carerId |
String "3" |
UUID | BFF must translate carer numeric ID → UUID via identity-service |
Assessment — GET /api/v1/assessments/:id
| Field | Mock shape | Real service shape | Action |
|---|---|---|---|
completionStatus |
"IN_PROGRESS" / "COMPLETED" |
status field in real service with different enum values |
Field rename |
patientId |
UUID reference | serviceUserId — same concept, different field name |
BFF renames |
assessmentId + id |
Both present (legacy compat) | Single id field |
BFF normalizes to single id |
overview |
Nested object { riskLevel, allergies, dnacp, nextAssessmentDate, ... } |
Not a single nested block — data spread across sections and risk-section endpoints | BFF must assemble from multiple sub-endpoints |
assessments |
Array of section objects with nested questions | Sections are separate sub-resources fetched via /assessments/{id}/sections |
BFF must aggregate |
| Header | No header in mock | X-VCC-User-Id, X-VCC-Organization-Id, X-VCC-User-Email required |
BFF forwards |
Care Plans — GET /api/v1/care-plans/:id
| Field | Mock shape | Real service shape | Action |
|---|---|---|---|
clientId |
String | Real service uses serviceUserId (UUID) |
Field rename in BFF adapter |
status values |
Custom string values | CarePlanStatus enum: draft, ai_generated, under_review, approved, active, suspended, completed, cancelled |
Map mock statuses to enum values |
| Server URL | Mock uses /api/v1/care-plans |
Real service base: https://api.vcc2.com/v1/api/v1/care-plans — note double prefix in careplan OpenAPI |
Confirm actual deployed base path |
tasks |
Embedded array in plan response | Separate resource: TaskCategory objects under Visit objects |
Frontend must navigate new structure |
Action required for all shape mismatches: Add an adapter/transformer class in the BFF for each domain when wiring controllers (Phase 1). Do not rely on the real service coincidentally matching the mock shape.
DA-4. Post-Migration Schema Audit
After Phase 3 (per-domain cutover) completes for each domain, run a schema audit before disabling the mock fallback for that domain.
Add to Phase 3 per-domain checklist:
Step 6 (new) — Schema audit for domain being cut over:
# Example for visits domain — run from a machine with curl access to BFF
BFF_URL="https://<web-bff-fqdn>.azurecontainerapps.io"
AUTH_TOKEN="<valid-bearer-token>"
# 1. Fetch a real response
curl -s -H "Authorization: Bearer $AUTH_TOKEN" \
"$BFF_URL/api/v1/visits?page=0&size=5" \
-o /tmp/real-visits-response.json
# 2. Validate against OpenAPI spec using swagger-cli or similar
npx @apidevtools/swagger-cli validate \
/Users/makinja/projects/client/lumiscare/openapi-specs/visits-open-api/open-api.yaml
# 3. Manual spot-check — verify required fields present
node -e "
const data = require('/tmp/real-visits-response.json');
const required = ['id','status','scheduledStartTime','scheduledEndTime','serviceUserId','carerId'];
const first = data.content?.[0] || data[0] || {};
required.forEach(f => {
if (first[f] === undefined) console.log('MISSING:', f);
else console.log('PRESENT:', f, '=', JSON.stringify(first[f]).slice(0, 40));
});
"
Acceptance criteria for schema audit:
- All required fields from OpenAPI spec are present in real response
- No 500 errors in Azure Application Insights for the domain in the last 30 minutes
- Frontend renders the domain view without console errors (visual check by developer)
Gate: Do not disable mock fallback for a domain until schema audit passes.
DA-5. Rollback Test (Phase 2.4)
The existing rollback plan (Section 6) describes the mechanism but never tests it. A rollback that has never been tested is not a reliable rollback.
Add Phase 2.4 — Rollback Test (run after Phase 2.3, before Phase 3)
Procedure:
- Confirm BFF fallback is enabled (
MOCK_API_FALLBACK_ENABLED=true) - Confirm frontend is pointing to BFF URL (Phase 2.1 complete)
- For one REAL_READY domain (recommended: Identity), disable the BFF route to real service:
- Comment out or stub the real service client for that domain in BFF
- Redeploy BFF (or use feature flag)
- Verify BFF fallback activates:
curl $BFF_URL/api/v1/service-usersshould return mock data (HTTP 200, mock IDs like"su-001")- Browser: service users page must render without error
- Re-enable the real service client
- Verify real data is served again
- Document the rollback time (target: under 5 minutes end-to-end including BFF redeploy)
Pass criteria:
- Fallback activates within one BFF redeploy cycle
- Frontend renders correctly on mock data during fallback
- Re-enable returns real data with no stale cache issues
Gate: If rollback test fails, do NOT proceed to Phase 3. Fix the fallback mechanism first.
DA-6. Phase Environment Matrix
At each phase, different environments run on different data sources. This was implicit in the original plan — made explicit here.
| Phase | Dev environment | Test environment | Staging environment | Production |
|---|---|---|---|---|
| Pre-migration (now) | Frontend → Mock direct | N/A | N/A | N/A |
| Phase 0 (prep) | Seed loaded in Azure dev DBs | Mock still running | Not affected | Not affected |
| Phase 1 (BFF wiring) | BFF code changes in dev branch | Mock fallback enabled | Not affected | Not affected |
| Phase 2 (frontend switch) | Frontend → BFF; BFF fallback ON for all routes | Frontend → BFF; BFF fallback ON | Not affected | Not affected |
| Phase 3 (per-domain cutover) | BFF routes real per domain; fallback OFF per domain after audit | BFF + fallback; promote domain-by-domain after dev validates | BFF + fallback per domain; reduced fallback set | Not affected until explicit prod migration |
| Phase 4 (decommission) | Mock stopped | Mock stopped | Mock stopped | N/A (mock never ran in prod) |
Environment-specific flags:
# application-dev.yml
mock-api:
fallback-url: ${MOCK_API_URL:http://ca-vcc-dev-mock-api:3000}
fallback-enabled: true
fallback-domains:
visits: true # flip to false after Phase 3 domain cutover
scheduling: true
notifications: true
family: true # stays true until FamilyPortalService exists
misc: true
# application-test.yml
mock-api:
fallback-url: ${MOCK_API_URL:http://ca-vcc-test-mock-api:3000}
fallback-enabled: true
fallback-domains:
visits: true
# ... test environment follows dev by ~1 sprint
# application-staging.yml
mock-api:
fallback-enabled: false # staging runs real services or explicit test data
fallback-domains: {} # no fallback in staging
# application-prod.yml
mock-api:
fallback-enabled: false # mock never deployed in prod
DA-7. Evidence Checklist per Domain (Cutover Gate)
For each domain cutover in Phase 3, the following evidence must be machine-generated before the mock fallback is disabled for that domain. "Works on my machine" and agent-reported PASS are not acceptable (ZAKON #21).
Template — fill out for each domain:
Domain: _______________
Date: _______________
Engineer: _______________
[ ] 1. seed-verify: DB row counts pass verify-seeds.sh for this domain's DB (exit 0)
[ ] 2. health-check: Container App status = Running
Evidence: az containerapp show --name <ca-name> --query properties.runningStatus
[ ] 3. direct-service: Direct HTTP call to real service returns 200
Evidence: curl -s -o /tmp/service-health.json -w "%{http_code}" http://<service-internal-url>/actuator/health
[ ] 4. bff-proxy: BFF routes traffic to real service (not mock)
Evidence: curl -s -H "Authorization: Bearer $TOKEN" $BFF_URL/api/v1/<domain-route> | head -c 200
Confirm: IDs are UUIDs, not mock format (e.g., "su-001")
[ ] 5. schema-audit: Required fields present in response (DA-4 procedure)
Evidence: node schema-audit.js /tmp/<domain>-response.json
[ ] 6. no-500s: Zero 500 errors in Azure Application Insights for domain in last 30 min
Evidence: az monitor app-insights query --app <app-insights-name> --analytics-query "..."
[ ] 7. frontend-render: Domain UI renders without console errors
Evidence: Screenshot or Playwright test output
[ ] 8. rollback-ready: MOCK_API_FALLBACK_ENABLED=true can be set within 5 min if needed
Evidence: Rollback test passed (Phase 2.4)
PASS if all 8 checked. FAIL = keep mock fallback for this domain.
Minimum bar per domain:
- Identity: All 8 required (everything else depends on auth)
- Assessment, Care Plans, Incidents, HR, Finance: Items 1-7 required
- Visits, Scheduling, Notifications: Items 1-7 required
- Family Portal: Skip — stays on mock (see DA-8)
DA-8. Family Portal ADR (Architecture Decision Record)
Decision required before Phase 3 begins.
Context
The Family Portal has 16 mock endpoints (/api/v1/family/*). There is no FamilyPortalService in the deployed microservice architecture. Building a proper BFF layer for family portal requires aggregating from:
careplan-service(read-only care plan view)notification-service(family notifications)identity-service(family member auth)- An unbuilt payments integration
Options
Option A — Build FamilyPortalBffController in web-bff (or new family-bff)
- Effort: High (3-5 sprints)
- Requires: New BFF controller wiring 3+ services; family member auth flow; potentially new microservice
- Risk: Derails MVP timeline
Option B — Scope out Family Portal from MVP; keep on mock fallback
- Effort: Zero
- Mock fallback continues serving family routes indefinitely until a future sprint
- Family portal users (family members) see the mock data — acceptable for beta/internal demo
- Decision reversible
Decision
Recommendation: Option B — Scope Family Portal out of MVP migration.
Family portal features are non-critical for the primary care agency workflow. The MVP needs the back-office and mobile carer workflows operational. Family portal is a Phase 2 deliverable.
Actions:
- Do NOT wire family portal routes to any real service during Phase 3
- Keep
mock-api.fallback-domains.family: truepermanently through MVP launch - Add
POST /api/v1/family/*routes to theMOCK_ONLYfallback list explicitly in BFF config - Create a new backlog item: "FamilyPortalService design + BFF wiring" — target: post-MVP sprint 1
- Timeline target for family portal real wiring: 6-8 weeks post-MVP launch
This ADR is a CEO-level decision. Tag for explicit sign-off before Phase 3 begins.
DA-9. Demo Protection
During Phase 3 and leading up to any investor demo or client presentation, certain routes must remain stable. A real service returning unexpected data or a 500 error during a demo is unacceptable.
Demo-Critical Routes
These routes are rendered on the first screens visible during a standard demo:
GET /api/v1/dashboard/stats
GET /api/v1/service-users
GET /api/v1/service-users/:id
GET /api/v1/care-plans
GET /api/v1/care-plans/:id
GET /api/v1/assessments
GET /api/v1/visits
GET /api/v1/incidents
GET /api/v1/notifications/inbox
GET /api/v1/notifications/inbox/unread-count
DEMO_MODE Flag
Add a DEMO_MODE environment flag to the BFF:
# application.yml (BFF)
demo:
mode: ${DEMO_MODE:false}
# When true: all demo-critical routes use mock fallback regardless of domain cutover status
# Overrides per-domain fallback-enabled settings for demo-critical routes only
protected-routes:
- /api/v1/dashboard/stats
- /api/v1/service-users
- /api/v1/service-users/**
- /api/v1/care-plans
- /api/v1/care-plans/**
- /api/v1/assessments
- /api/v1/visits
- /api/v1/incidents
- /api/v1/notifications/inbox
- /api/v1/notifications/inbox/unread-count
BFF routing logic (pseudo-code):
// In BFF route handler
if (demoMode && demoProtectedRoutes.matches(request.getPath())) {
return mockFallback.forward(request); // always use mock for demo routes in DEMO_MODE
}
// Otherwise: normal real-service routing
return realService.forward(request);
48-Hour Stability Rule
For demo-critical routes, do not disable the mock fallback until the real service has been stable for 48 hours with zero 5xx errors. Evidence required: Azure Application Insights query showing error rate = 0 for 48h window.
# Check 48h stability for visits domain
az monitor app-insights query \
--app <app-insights-resource> \
--analytics-query "
requests
| where timestamp > ago(48h)
| where url contains '/api/v1/visits'
| where resultCode startswith '5'
| count
"
# Expected output: count = 0
Demo Runbook
Before any investor demo or client presentation:
- Set
DEMO_MODE=truein BFF Container App environment (Azure Portal oraz containerapp update) - Verify dashboard loads with realistic mock data
- Do NOT disable
DEMO_MODEuntil demo is complete - After demo: reset
DEMO_MODE=falseto resume real-service routing
DA-1. Endpoint Priority Matrix
All 204 addressable mock endpoints classified by migration readiness. Wildcards and catch-alls excluded.
Classification key:
- REAL_READY — Spring Boot service has the endpoint AND BFF controller is wired
- NEEDS_WIRING — Service has the endpoint but BFF controller is missing or incomplete
- MOCK_ONLY — No real service implementation exists; keep mock fallback
- SCOPE_OUT — Feature is not needed for MVP; disable after migration without replacement
Visits (14 endpoints)
| Mock Endpoint | Classification | Notes |
|---|---|---|
GET /api/v1/visits |
NEEDS_WIRING | visits-service /visits exists; BFF controller missing |
POST /api/v1/visits |
NEEDS_WIRING | visits-service /visits POST exists; BFF controller missing |
GET /api/v1/visits/:id |
NEEDS_WIRING | visits-service /visits/{visitId} exists |
PATCH /api/v1/visits/:id |
NEEDS_WIRING | visits-service PATCH exists |
DELETE /api/v1/visits/:id |
NEEDS_WIRING | visits-service DELETE exists |
POST /api/v1/visits/:id/check-in |
NEEDS_WIRING | visits-service check-in exists |
POST /api/v1/visits/:id/check-out |
NEEDS_WIRING | visits-service check-out exists |
GET /api/v1/visits/:id/emar |
NEEDS_WIRING | visits-service eMAR exists |
GET /api/v1/visits/:id/tasks |
NEEDS_WIRING | visits-service tasks list exists |
GET /api/v1/visits/today |
NEEDS_WIRING | visits-service /visits/today defined |
POST /api/v1/visits/publish |
MOCK_ONLY | No publish concept in visits-service OpenAPI |
POST /api/v1/visits/:id/running-late |
MOCK_ONLY | No equivalent in visits-service spec |
GET /api/v1/visits/:id/pre-visit-brief |
MOCK_ONLY | Aggregated BFF feature, not a service endpoint |
POST /api/v1/visits/:id/travel-record |
MOCK_ONLY | Tracking endpoint in visits-service is different shape |
Scheduling (8 endpoints)
| Mock Endpoint | Classification | Notes |
|---|---|---|
GET /api/v1/schedule-blocks |
NEEDS_WIRING | Maps to /schedule-events in scheduling-service; CalendarController exists but not wired for this alias |
POST /api/v1/schedule-blocks |
NEEDS_WIRING | Maps to POST /schedule-events |
GET /api/v1/schedule-blocks/:id |
NEEDS_WIRING | Maps to /schedule-events/{eventId} |
PATCH /api/v1/schedule-blocks/:id |
NEEDS_WIRING | Maps to PUT /schedule-events/{eventId} |
DELETE /api/v1/schedule-blocks/:id |
NEEDS_WIRING | Maps to DELETE /schedule-events/{eventId} |
GET /api/v1/completed-events |
NEEDS_WIRING | Maps to /appointments with status=completed filter |
POST /api/v1/completed-events/:id/confirm |
NEEDS_WIRING | Maps to POST /appointments/{id}/cancel pattern (status update) |
POST /api/v1/completed-events/bulk-confirm |
MOCK_ONLY | No bulk-confirm operation in scheduling-service spec |
Assessment (12 endpoints)
| Mock Endpoint | Classification | Notes |
|---|---|---|
GET /api/v1/assessments |
REAL_READY | BffAssessmentController wired |
GET /api/v1/assessments/:id |
REAL_READY | BffAssessmentController wired |
POST /api/v1/assessments |
REAL_READY | BffAssessmentController wired |
PUT /api/v1/assessments/:id |
REAL_READY | BffAssessmentController wired |
PATCH /api/v1/assessments/:id |
REAL_READY | BffAssessmentController wired |
POST /api/v1/assessments/:id/complete |
REAL_READY | Maps to assessment submit endpoint |
GET /api/v1/assessments/:id/body-map |
NEEDS_WIRING | assessment-service has body-map section; BFF wiring unconfirmed |
PUT /api/v1/assessments/:id/body-map |
NEEDS_WIRING | as above |
GET /api/v1/assessments/:id/scores |
MOCK_ONLY | No scoring endpoint in assessment-service spec; maps to risk-section concept |
GET /api/v1/assessments/:id/scores/:tool |
MOCK_ONLY | No tool-specific scores in spec |
POST /api/v1/assessments/:id/scores/:tool |
MOCK_ONLY | No tool-specific scores in spec |
GET /api/v1/intake |
REAL_READY | assessment-service /api/v1/intake matches |
Care Plans (14 endpoints)
| Mock Endpoint | Classification | Notes |
|---|---|---|
GET /api/v1/care-plans |
REAL_READY | CarePlanController wired |
GET /api/v1/care-plans/:id |
REAL_READY | CarePlanController wired |
POST /api/v1/care-plans |
REAL_READY | CarePlanController wired |
PUT /api/v1/care-plans/:id |
REAL_READY | CarePlanController wired |
DELETE /api/v1/care-plans/:id |
REAL_READY | CarePlanController wired |
POST /api/v1/care-plans/:id/publish |
REAL_READY | careplan-service publish endpoint exists |
GET /api/v1/care-plans/:id/versions |
REAL_READY | careplan-service versions endpoint exists |
GET /api/v1/care-plans/review-due |
MOCK_ONLY | No review-due filter in careplan-service spec |
POST /api/v1/care-plans/:id/submit-for-approval |
MOCK_ONLY | No submit-for-approval step in real spec |
GET /api/v1/care-plans/:id/tasks |
MOCK_ONLY | Task management not in careplan-service OpenAPI |
GET /api/v1/care-plans/:id/risk-alerts |
MOCK_ONLY | No risk-alerts endpoint in careplan-service |
GET /api/v1/care-plans/:id/reviews |
MOCK_ONLY | No reviews endpoint in careplan-service |
POST /api/v1/care-plans/:id/consent |
MOCK_ONLY | No consent endpoint in careplan-service |
GET /api/v1/master-task-types |
REAL_READY | careplan-service has master-task-types |
HR (18 endpoints)
| Mock Endpoint | Classification | Notes |
|---|---|---|
GET /employees/list |
REAL_READY | EmployeeController wired (legacy path) |
GET /employees/:id |
REAL_READY | EmployeeController wired |
POST /employees |
REAL_READY | EmployeeController wired |
PATCH /employees/:id |
REAL_READY | EmployeeController wired |
DELETE /employees/:id |
REAL_READY | EmployeeController wired |
GET /api/v1/leaves |
REAL_READY | hr-service leaves endpoint exists; BFF wired |
POST /api/v1/leaves |
REAL_READY | hr-service leaves POST exists |
PATCH /api/v1/leaves/:id |
REAL_READY | hr-service leaves PATCH exists |
DELETE /api/v1/leaves/:id |
REAL_READY | hr-service leaves DELETE exists |
GET /api/v1/hr/supervisions |
NEEDS_WIRING | hr-service has supervisions; BFF sub-route not wired |
GET /api/v1/hr/supervision-schedule |
NEEDS_WIRING | hr-service has supervision data; path mapping needed |
GET /api/v1/hr/absences |
NEEDS_WIRING | hr-service has absence/leave data; path alias needed |
GET /api/v1/hr/training |
NEEDS_WIRING | hr-service has training; BFF route not wired |
GET /api/v1/training/courses |
NEEDS_WIRING | hr-service training module; BFF path different |
GET /api/v1/hr/dbs |
MOCK_ONLY | DBS checks not in hr-service OpenAPI as named endpoint |
GET /api/v1/hr/bradford |
MOCK_ONLY | Calculated aggregate; no endpoint in hr-service |
GET /api/v1/hr/care-certificate |
MOCK_ONLY | Not in hr-service OpenAPI |
GET /api/v1/hr/cqc-inspection-pack |
SCOPE_OUT | Complex regulatory aggregate; not MVP |
Finance (20 endpoints)
| Mock Endpoint | Classification | Notes |
|---|---|---|
GET /api/v1/finance/invoices |
REAL_READY | FinanceBffController wired |
GET /api/v1/finance/invoices/:id |
REAL_READY | FinanceBffController wired |
POST /api/v1/finance/invoices |
REAL_READY | FinanceBffController wired |
PUT /api/v1/finance/invoices/:id |
REAL_READY | FinanceBffController wired |
GET /api/v1/finance/invoice-groups |
REAL_READY | finance-service invoice-groups; BFF wired |
POST /api/v1/finance/invoice-groups |
REAL_READY | finance-service wired |
GET /api/v1/finance/payment-groups |
REAL_READY | finance-service wired |
POST /api/v1/finance/payment-groups |
REAL_READY | finance-service wired |
GET /api/v1/finance/timesheets |
NEEDS_WIRING | finance-service has timesheets; BFF sub-route unconfirmed |
GET /api/v1/finance/dashboard |
NEEDS_WIRING | finance-service dashboard exists; aggregation needed |
GET /api/v1/finance/rate-cards |
MOCK_ONLY | Not in finance-service OpenAPI |
GET /api/v1/finance/funders |
MOCK_ONLY | Not in finance-service OpenAPI |
GET /api/v1/finance/payroll/preview |
MOCK_ONLY | Payroll is separate domain; not in MVP |
POST /api/v1/finance/payroll/approve |
MOCK_ONLY | Payroll not in MVP |
GET /api/v1/finance/payroll/export |
SCOPE_OUT | Out of MVP scope |
GET /api/v1/finance/aged-debtors |
MOCK_ONLY | Calculated from invoices; no dedicated endpoint |
GET /api/v1/finance/nmw-compliance |
SCOPE_OUT | Compliance report; out of MVP scope |
GET /api/v1/finance/month-end-status |
MOCK_ONLY | No equivalent in finance-service spec |
GET /api/v1/finance/bank-holidays |
MOCK_ONLY | Static data; serve from BFF config |
GET /api/v1/invoices |
REAL_READY | Legacy path; maps to finance-service invoices |
Incidents (8 endpoints)
| Mock Endpoint | Classification | Notes |
|---|---|---|
GET /api/v1/incidents |
NEEDS_WIRING | Real service has typed routes (/accidents, /safeguarding, /complaints, /hazards); BFF must aggregate or route by type |
GET /api/v1/incidents/:id |
NEEDS_WIRING | BFF must determine type and proxy to correct sub-service |
POST /api/v1/incidents |
NEEDS_WIRING | BFF must route by type field to correct incident sub-resource |
PATCH /api/v1/incidents/:id |
NEEDS_WIRING | Same routing requirement |
DELETE /api/v1/incidents/:id |
NEEDS_WIRING | Same routing requirement |
GET /api/v1/incidents/summary |
REAL_READY | incidents-service /summary endpoint exists; IncidentsController wired |
POST /api/v1/incidents/:id/actions |
NEEDS_WIRING | incidents-service log-actions endpoint exists; BFF wiring unconfirmed |
GET /api/v1/incidents/export |
SCOPE_OUT | Export for each sub-type exists in spec; aggregate export not MVP |
Notifications (10 endpoints)
| Mock Endpoint | Classification | Notes |
|---|---|---|
GET /api/v1/notifications/inbox |
NEEDS_WIRING | notification-service inbox exists; BFF controller missing entirely |
GET /api/v1/notifications/inbox/unread-count |
NEEDS_WIRING | notification-service unread-count exists; BFF missing |
PATCH /api/v1/notifications/inbox/:id/read |
NEEDS_WIRING | notification-service mark-read exists; BFF missing |
PATCH /api/v1/notifications/inbox/read-all |
NEEDS_WIRING | notification-service mark-all-read exists; BFF missing |
GET /api/v1/notifications/preferences |
NEEDS_WIRING | notification-service preferences exists; BFF missing |
POST /api/v1/notifications/preferences |
NEEDS_WIRING | notification-service preferences POST exists; BFF missing |
PATCH /api/v1/notifications/preferences/quiet-hours |
NEEDS_WIRING | notification-service; BFF missing |
POST /api/v1/notifications/devices |
NEEDS_WIRING | notification-service device registration; BFF missing |
POST /api/v1/notifications/push |
NEEDS_WIRING | notification-service send endpoint; BFF missing |
GET /api/v1/notifications/analytics |
MOCK_ONLY | Not in notification-service spec |
Identity / Users / Orgs / Service Users (14 endpoints)
| Mock Endpoint | Classification | Notes |
|---|---|---|
GET /api/v1/service-users |
REAL_READY | ServiceUsersController wired; identity-service backs it |
GET /api/v1/service-users/:id |
REAL_READY | ServiceUsersController wired |
POST /api/v1/service-users |
REAL_READY | ServiceUsersController wired |
PATCH /api/v1/service-users/:id |
REAL_READY | ServiceUsersController wired |
DELETE /api/v1/service-users/:id |
REAL_READY | ServiceUsersController wired |
GET /api/v1/users |
NEEDS_WIRING | identity-service users endpoint exists; BFF route may not be exposed |
GET /api/v1/organizations |
NEEDS_WIRING | identity-service orgs endpoint exists; BFF route unconfirmed |
GET /api/v1/organizations/:id |
NEEDS_WIRING | identity-service; BFF unconfirmed |
GET /api/v1/settings |
NEEDS_WIRING | identity-service org settings; BFF route unconfirmed |
PATCH /api/v1/settings |
NEEDS_WIRING | identity-service org settings PATCH; BFF unconfirmed |
GET /api/v1/carers |
NEEDS_WIRING | Maps to identity-service users with carer role filter |
POST /api/v1/carers/by-skills |
MOCK_ONLY | No skills-based search in identity-service spec |
GET /api/v1/skills |
MOCK_ONLY | No dedicated skills list endpoint in identity-service |
GET /api/v1/rostering-settings |
MOCK_ONLY | Not in identity-service spec |
Family Portal (16 endpoints)
| Mock Endpoint | Classification | Notes |
|---|---|---|
ALL GET /api/v1/family/* |
MOCK_ONLY | No FamilyPortalService exists. See DA-8 ADR below. |
Misc / EVV / Dashboard / Compliance (24 endpoints)
| Mock Endpoint | Classification | Notes |
|---|---|---|
GET /api/v1/dashboard/stats |
NEEDS_WIRING | Aggregate from multiple services; BFF orchestration needed |
GET /api/v1/cqc-readiness |
MOCK_ONLY | Aggregate; not in any service spec |
GET /api/v1/evv/compliance |
NEEDS_WIRING | Maps to visits-service tracking data; BFF aggregation needed |
GET /api/v1/evv/records |
NEEDS_WIRING | Maps to visits-service location tracking history |
GET /api/v1/evv/records/:id |
NEEDS_WIRING | Maps to visits-service tracking detail |
GET /api/v1/audit-logs |
MOCK_ONLY | No audit service in current architecture |
GET /api/v1/insurance/claims |
SCOPE_OUT | Not in architecture; park for post-MVP |
GET /api/v1/insurance/payers |
SCOPE_OUT | Not in architecture; park for post-MVP |
POST /api/v1/handoff/notes |
NEEDS_WIRING | Maps to visits-service care notes with type=handover |
GET /api/v1/handoff/history |
NEEDS_WIRING | Maps to visits-service care notes filtered by type |
POST /api/v1/handoff/notes/:id/acknowledge |
MOCK_ONLY | No acknowledgement concept in visits-service notes |
GET /api/v1/observations |
NEEDS_WIRING | Maps to assessment-service or visits-service observations |
GET /api/v1/supervisor/dashboard |
MOCK_ONLY | Aggregate view; BFF composition needed post-MVP |
GET /api/v1/supervisor/dashboard/carer-locations |
NEEDS_WIRING | Maps to visits-service /tracking/locations/current |
POST /api/v1/alerts/:id/acknowledge |
MOCK_ONLY | No alert acknowledgement service |
GET /api/v1/clients/:id/outcomes |
MOCK_ONLY | No outcomes service |
GET /api/v1/clients/:id/care-summary |
MOCK_ONLY | BFF aggregate; not MVP priority |
GET /api/v1/training/compliance |
NEEDS_WIRING | Maps to hr-service training compliance data |
GET /api/v1/care-alerts/:id/acknowledge |
MOCK_ONLY | No care-alerts service |
| ALL others (wildcards) | SCOPE_OUT | Catch-all routes; decommission with mock |
Priority Matrix Summary
| Classification | Count | Action |
|---|---|---|
| REAL_READY | ~47 | Verify in Phase 3; disable mock fallback per-route |
| NEEDS_WIRING | ~68 | Phase 1 + Phase 3 BFF work |
| MOCK_ONLY | ~63 | Keep mock fallback; build real service post-MVP |
| SCOPE_OUT | ~26 | Disable with no replacement; document as out-of-scope |
DA-2. Seed Data Verification (Phase 0.4)
Add this phase between Phase 0.1 (seed load) and Phase 0.2 (container health check).
Phase 0.4 — Verify Seed Data Loaded Correctly
After running seed scripts via Azure Cloud Shell, verify row counts before proceeding to Phase 1.
Expected minimum row counts per database:
| Database | Table | Minimum Rows | Purpose |
|---|---|---|---|
identity_db |
organizations |
1 | Test org |
identity_db |
users |
5 | 1 admin, 1 care-manager, 3 carers |
identity_db |
service_users |
3 | Test clients |
identity_db |
roles |
12 | Pre-seeded system roles |
assessment_db |
assessment_sections |
7 | ASSESSMENT_SECTION_DEFS equivalent |
assessment_db |
risk_sections |
6 | RISK_SECTION_DEFS equivalent |
careplan_db |
master_task_types |
10 | Task type catalogue |
careplan_db |
master_visit_types |
4 | Visit type catalogue |
visits_db |
visit_statuses |
7 | scheduled, late, in_progress, completed, cancelled, missed, flagged |
hr_db |
leave_types |
5 | Annual, sick, maternity, etc. |
hr_db |
training_categories |
4 | Mandatory, CPD, compliance, specialist |
scheduling_db |
appointment_types |
4 | care_visit, assessment_visit, medication_only, welfare_check |
finance_db |
invoice_statuses |
5 | Draft, issued, paid, overdue, cancelled |
incidents_db |
incident_type_config |
4 | accidents, safeguarding, complaints, hazards |
notification_db |
notification_templates |
5 | Core template types |
Verification queries — run in Azure Cloud Shell after seed:
-- identity_db
SELECT 'organizations' as tbl, count(*) FROM organizations
UNION ALL SELECT 'users', count(*) FROM users
UNION ALL SELECT 'service_users', count(*) FROM service_users
UNION ALL SELECT 'roles', count(*) FROM roles;
-- assessment_db
SELECT 'assessment_sections', count(*) FROM assessment_sections
UNION ALL SELECT 'risk_sections', count(*) FROM risk_sections;
-- careplan_db
SELECT 'master_task_types', count(*) FROM master_task_types
UNION ALL SELECT 'master_visit_types', count(*) FROM master_visit_types;
-- visits_db
SELECT 'visit_task_types', count(*) FROM visit_task_types;
-- incidents_db
SELECT table_name, 0 as dummy FROM information_schema.tables
WHERE table_schema = 'public'; -- verify tables exist
-- finance_db
SELECT 'invoice_line_items_config', count(*) FROM invoice_statuses;
Verify-seeds script spec:
Create /Users/makinja/projects/client/lumiscare/scripts/verify-seeds.sh:
#!/bin/bash
# verify-seeds.sh — run from Azure Cloud Shell after seed load
# Usage: bash verify-seeds.sh <pg-host> <admin-user>
PG_HOST="$1"
PG_USER="$2"
FAIL=0
check() {
local DB=$1 TBL=$2 MIN=$3
COUNT=$(psql "host=$PG_HOST user=$PG_USER dbname=$DB sslmode=require" \
-tAc "SELECT count(*) FROM $TBL 2>/dev/null || echo 0")
if [ "$COUNT" -lt "$MIN" ]; then
echo "FAIL: $DB.$TBL has $COUNT rows (expected >= $MIN)"
FAIL=1
else
echo "PASS: $DB.$TBL = $COUNT rows"
fi
}
check identity_db organizations 1
check identity_db users 5
check identity_db service_users 3
check assessment_db assessment_sections 7
check careplan_db master_task_types 10
check visits_db visit_task_types 4
check finance_db invoice_statuses 3
check incidents_db incident_categories 4
[ $FAIL -eq 0 ] && echo "ALL SEED CHECKS PASSED" || { echo "SEED VERIFICATION FAILED — do not proceed to Phase 1"; exit 1; }
Seed verification must pass before Phase 1 work begins. If any check fails, re-run the corresponding seed script.
DA-3. Shape Mismatch Matrix
Critical field-level differences between mock API response shapes and real OpenAPI-specified shapes. BFF adapter layer must translate these on cutover.
Visits — GET /api/v1/visits
| Field | Mock Shape | Real Service Shape (visits-service OpenAPI) | BFF Action |
|---|---|---|---|
| List wrapper | [...visits] (bare array) |
{ "visits": [...], "pageInfo": { "total": N, "page": 0, "size": 20 } } |
BFF must unwrap visits array for frontend; frontend expects array |
id |
"visit-0001" (string, dash-prefixed) |
UUID format "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" |
ID format will change; frontend must handle UUID |
clientId |
"su-001" (string, dash-prefixed) |
serviceUserId (UUID) |
Field rename: clientId → serviceUserId |
clientName |
"Dorothy Johnson" (string in visit object) |
Not in visit object — join required from identity-service | BFF must enrich with identity-service lookup or accept that frontend needs separate call |
status |
"in-progress" (kebab-case) |
"in_progress" (snake_case enum: scheduled, late, in_progress, completed, cancelled, missed, flagged) |
BFF must normalize status values |
type |
"personal-care" (kebab-case, free string) |
appointmentType (enum: care_visit, assessment_visit, medication_only, welfare_check) |
Field rename + value normalization |
gpsCheckIn |
{ lat: 51.5074, lng: -0.1278 } |
{ "latitude": 51.5074, "longitude": -0.1278, "accuracy": 5.0, "timestamp": "..." } (GpsLocation schema) |
Field rename: lat→latitude, lng→longitude; add required fields |
careNotes |
String field on visit object | Separate endpoint GET /visits/{visitId}/notes → [{ "noteType": "...", "content": "..." }] |
Notes must be fetched separately; BFF may aggregate or frontend must call separately |
actualCheckIn / actualCheckOut |
ISO string on visit object | Nested in check-in/check-out response objects, not returned on visit GET | BFF must compose from visit + lifecycle data |
Scheduling — GET /api/v1/schedule-blocks
| Field | Mock Shape | Real Service Shape (scheduling-service OpenAPI) | BFF Action |
|---|---|---|---|
| Resource name | schedule-blocks (array of blocks) |
schedule-events (ScheduleEvent objects) |
Path alias handled by BFF |
type |
"team-meeting", "training", "supervision", "appraisal" (kebab-case) |
ScheduleEventType enum: meeting, training, supervision, appraisal (no dash, no team- prefix) |
BFF must normalize team-meeting → meeting |
duration |
Integer (minutes) | Not a top-level field; calculated from startDateTime and endDateTime |
BFF may compute duration from datetime diff |
startTime / endTime |
ISO string | startDateTime / endDateTime (ISO 8601 with timezone) |
Field rename in BFF adapter |
| List wrapper | Bare array | Paginated: { "content": [...], "page": 0, "size": 20, "totalElements": N, "totalPages": N } |
BFF extracts content or adapts to paginated format |
Incidents — GET /api/v1/incidents
| Field | Mock Shape | Real Service Shape (incidents-service OpenAPI) | BFF Action |
|---|---|---|---|
| List wrapper | { "incidents": [...], "total": N } |
Per type: { "content": [...], "page": 0, "totalElements": N, "totalPages": N } |
BFF must aggregate responses from /accidents, /safeguarding, /complaints, /hazards |
id |
"inc-001" (string) |
UUID (auto-generated) | ID format change; BFF cannot map old IDs |
type |
"Accident", "Medication_Error", "Safeguarding" |
Separate resources by route; type determined by endpoint (/accidents vs /safeguarding) |
BFF adds type field when aggregating |
incident_number |
"INC-2024-001" |
referenceNumber field: "ACC-2026-00001" (typed prefix format) |
Field rename + prefix format changes |
severity |
"High", "Medium", "Critical" (title-case) |
SeverityLevel enum: HIGH, MEDIUM, LOW, CRITICAL (UPPER_CASE) |
BFF normalizes to uppercase |
status |
"Resolved", "Under_Review" (mixed case) |
LogStatus enum: IDENTIFIED, RAISED, IN_PROGRESS, RESOLVED etc. (UPPER_CASE) |
BFF normalizes case; Under_Review → IN_PROGRESS |
dateAndTime |
ISO string | incidentDatetime (ISO 8601 with Z suffix) |
Field rename in BFF |
actionsTaken |
Array on the incident object | Separate endpoint: GET /logs/{logType}/{logId}/actions |
Actions must be fetched separately |
has_investigation |
Boolean | Not in incidents-service OpenAPI — derived from status | BFF computes from status |
Care Plans — GET /api/v1/care-plans
| Field | Mock Shape | Real Service Shape (careplan-service OpenAPI) | BFF Action |
|---|---|---|---|
clientId |
"su-001" |
clientId (UUID) |
Same field name; ID format changes to UUID |
status |
"active", "draft", "under_review" |
CarePlanStatus enum: draft, ai_generated, under_review, approved, active, suspended, completed, cancelled |
Values align but ai_generated is new — frontend must handle |
| Tasks nested | tasks: [...] array on care plan |
Tasks NOT on care plan object in careplan-service spec; separate route /care-plan/{id}/tasks |
BFF may aggregate or frontend must call separately |
| Visit structure | visits: [...] array |
CarePlanDetailsResponse has Visit[] with nested TaskCategory[] and CarePlanTask[] |
Nested structure differs significantly from mock flat array |
Assessment — GET /api/v1/assessments/:id
| Field | Mock Shape | Real Service Shape (assessment-service OpenAPI) | BFF Action |
|---|---|---|---|
id vs assessmentId |
Both id and assessmentId present (legacy dual fields) |
assessmentId only (UUID) |
Remove dual field; use assessmentId consistently |
patientId |
UUID string | patientId (UUID) — matches |
No change needed |
completionStatus |
"IN_PROGRESS", "COMPLETED" |
completionStatus enum per spec |
Matches — no change |
lastUpdated |
Concatenated date + literal T10:30:00Z suffix |
ISO 8601 datetime from service | Remove hardcoded suffix hack |
overview |
Nested object with riskLevel, allergies, etc. |
Not a direct response field — derived from assessment sections | BFF must compose overview from section data |
DA-4. Schema Validation (Post-Migration Audit Step)
Add as Phase 3.5, after all per-domain cutovers complete but before Phase 4 decommission.
Phase 3.5 — Schema Audit
Objective: Machine-verify that every active BFF response matches its OpenAPI contract. No visual inspection.
Step 1 — Generate JSON Schemas from OpenAPI specs:
# Using openapi-schema-validator or ajv-openapi
npx @apidevtools/swagger-parser validate \
/Users/makinja/projects/client/lumiscare/openapi-specs/visits-open-api/open-api.yaml
# Repeat for each service spec
Step 2 — Record BFF responses against schema:
For each REAL_READY or newly NEEDS_WIRING domain, run automated contract tests:
# Install dredd or schemathesis
pip install schemathesis
# Run against BFF with real backend:
schemathesis run \
/Users/makinja/projects/client/lumiscare/openapi-specs/visits-open-api/open-api.yaml \
--base-url https://<web-bff-fqdn>.azurecontainerapps.io \
--auth-type bearer --auth "$JWT_TOKEN" \
--header "X-VCU-Organization-Id: $ORG_ID"
Step 3 — Adapter correction loop:
For each schema violation found:
- Identify if the mismatch is in the BFF adapter or the service response
- Fix the BFF adapter layer (preferred) or raise a service ticket
- Re-run schemathesis until zero schema violations remain
Step 4 — Acceptance criteria:
- Zero schemathesis ERRORS (hard failures) for all REAL_READY routes
- WARNINGS documented and accepted or fixed before Phase 4
- Schema audit report saved to
/Users/makinja/projects/client/lumiscare/docs/schema-audit-report.md
Pass criteria for Phase 4 entry: Schema audit exits with code 0 for all 10 domains.
DA-5. Rollback Test (Phase 2.4)
Add between Phase 2.3 (keep mock running) and Phase 3 (per-domain cutover).
Phase 2.4 — Validate Rollback Mechanism Works
Before committing to Phase 3 cutovers, prove the rollback path is functional. This is a one-time gate check.
2.4.1 Simulate a domain failure:
# Disable visits-service in BFF by setting a bad URL (or scale to 0)
az containerapp update \
--name ca-vcc-dev-visits-service-001 \
--resource-group rg-vcc-dev-001 \
--min-replicas 0 --max-replicas 0
2.4.2 Verify BFF fallback activates automatically:
# Call a visits endpoint through BFF — should return mock data, not 502
curl -s -o /dev/null -w "%{http_code}" \
-H "Authorization: Bearer $JWT_TOKEN" \
https://<web-bff-fqdn>.azurecontainerapps.io/api/v1/visits
# Expected: 200 (served from mock fallback)
2.4.3 Verify fallback data is recognizable (not silent corruption):
# Response should include mock data markers (e.g., "visit-0001" style IDs)
curl -s -H "Authorization: Bearer $JWT_TOKEN" \
https://<web-bff-fqdn>.azurecontainerapps.io/api/v1/visits \
| jq '.[0].id'
# Expected: "visit-0001" or similar mock ID pattern
2.4.4 Re-enable the service and confirm real data returns:
az containerapp update \
--name ca-vcc-dev-visits-service-001 \
--resource-group rg-vcc-dev-001 \
--min-replicas 1 --max-replicas 3
# Wait for healthy status, then re-test
curl -s -H "Authorization: Bearer $JWT_TOKEN" \
https://<web-bff-fqdn>.azurecontainerapps.io/api/v1/visits \
| jq '.[0].id'
# Expected: UUID format (real service data)
2.4.5 Document rollback timings:
| Rollback Type | Expected Time | Acceptable Max |
|---|---|---|
BFF env var toggle (MOCK_API_FALLBACK_ENABLED=true) |
< 2 minutes | 5 minutes |
| Per-domain BFF route disable (redeploy BFF) | 3-5 minutes | 10 minutes |
| Frontend env var change (Static Web App) | < 5 minutes | 15 minutes |
| Database restore from Azure backup | 30-60 minutes | 2 hours |
Gate: Phase 2.4 must PASS before any Phase 3 cutover proceeds.
DA-6. Phase Timing and Environment Clarity
Which systems run on what at each migration phase, per environment.
Environment Matrix by Phase
| Phase | Dev | Test | Staging | Production |
|---|---|---|---|---|
| Current (pre-migration) | Frontend → Mock API direct | Frontend → Mock API direct | N/A | N/A |
| Phase 0 (seed + prep) | Mock API still direct; seeds loaded to Azure DBs | No change | No change | No change |
| Phase 1 (BFF wiring) | BFF development; Mock API still used by frontend | Mock API still used | No change | No change |
| Phase 2 (frontend switch) | Frontend → BFF (BFF → real service OR mock fallback) | Frontend → BFF (all fallback) | BFF deployed, fallback enabled for all routes | Not deployed |
| Phase 3 (per-domain cutover) | BFF → real services per-domain; fallback only for MOCK_ONLY | BFF + per-domain fallback; real services tested here | BFF + fallback per-domain; each domain promoted after test passes | Not deployed |
| Phase 4 (decommission) | BFF → real services; fallback disabled; mock stopped | Mock stopped | Mock stopped | Deploy begins post-stability |
Service URL Routing by Environment
Dev: Frontend → https://<bff-dev>.azurecontainerapps.io → {service}-dev → identity_db_dev
Test: Frontend → https://<bff-test>.azurecontainerapps.io → {service}-test → identity_db_test
Staging: Frontend → https://<bff-stage>.azurecontainerapps.io → {service}-stage → identity_db_stage
Prod: Frontend → https://<bff-prod>.azurecontainerapps.io → {service}-prod → identity_db_prod
Fallback Configuration by Environment
| Environment | MOCK_API_FALLBACK_ENABLED |
MOCK_API_URL |
Notes |
|---|---|---|---|
| Dev | true during Phase 1-3; false at Phase 4 |
http://mock-api-dev:3000 |
Toggle per domain using route-level flags |
| Test | true until domain passes test suite; then false |
http://mock-api-test:3000 |
Automated test gate controls the toggle |
| Staging | true for 48h stability window per domain; then false |
http://mock-api-stage:3000 |
See DA-9 Demo Protection |
| Production | false from day one (start clean) |
N/A | No mock in production ever |
DA-7. Evidence Checklist per Domain (Cutover Gate)
For each domain in Phase 3, the following machine-verified criteria must ALL pass before disabling the mock fallback for that domain. Evidence artifacts must be saved.
Cutover Checklist Template
Replace {DOMAIN} with the domain name (e.g., assessment, visits).
[ ] 1. Seed data verified — verify-seeds.sh passes for {DOMAIN}_db
[ ] 2. Service health — GET /actuator/health returns {"status":"UP"} (HTTP 200)
[ ] 3. BFF route active — GET /api/v1/{domain-path} returns HTTP 200 (not 502 or fallback)
[ ] 4. Auth propagation — JWT + org header accepted; 401 returned without token
[ ] 5. Data shape — schemathesis or equivalent schema check passes (zero violations)
[ ] 6. CRUD smoke — POST creates, GET retrieves, PATCH updates, DELETE removes (where applicable)
[ ] 7. Pagination — List endpoint returns paginated wrapper, not raw array
[ ] 8. Tenant isolation — org-A data not visible when calling with org-B token
[ ] 9. Error format — 404 returns standardized error schema, not mock `{ error: "Not found" }`
[ ] 10. Fallback disabled — MOCK_API_FALLBACK_ENABLED=false for this domain; mock NOT serving requests
[ ] 11. Playwright smoke tests — existing frontend test suite passes against BFF (no regressions)
[ ] 12. Rollback verified — fallback re-enable tested and confirmed working (DA-5 gate)
Evidence artifacts per domain:
| Artifact | Format | Location |
|---|---|---|
| verify-seeds output | .txt log |
/docs/migration-evidence/{domain}-seed-verify.txt |
| schemathesis report | JSON or HTML | /docs/migration-evidence/{domain}-schema-report.json |
| Playwright test report | JUnit XML or HTML | CI artifact |
| Cutover timestamp | ISO 8601 string | /docs/migration-evidence/{domain}-cutover.json |
Domain cutover sign-off: All 12 checks must show machine-generated PASS. No self-certification. DevOps engineer reviews artifact paths before toggling MOCK_API_FALLBACK_ENABLED=false.
Domain Priority and Cutover Order
| Priority | Domain | Blocker For | Estimated Cutover Date (relative) |
|---|---|---|---|
| 1 | Identity | All others — auth depends on it | Week 1 Day 1 |
| 2 | Assessment | Care Plans, clinical workflow | Week 1 Day 3 |
| 3 | Care Plans | Visits task planning | Week 1 Day 5 |
| 4 | Visits | Mobile carers, eMAR | Week 2 Day 1 |
| 5 | Incidents | Safeguarding compliance | Week 2 Day 2 |
| 6 | HR | Staff compliance reporting | Week 2 Day 3 |
| 7 | Scheduling | Calendar view, rostering | Week 2 Day 4 |
| 8 | Notifications | Non-blocking but improves UX | Week 2 Day 5 |
| 9 | Finance | Invoicing, billing | Week 3 Day 1 |
| 10 | Family Portal | MOCK_ONLY — stays on mock | Post-MVP |
DA-8. Family Portal Architecture Decision Record (ADR-001)
Date: 2026-03-26 Status: ACCEPTED Deciders: Engineering Lead, Product Owner
Context
The Family Portal frontend (GET /api/v1/family/*) has 16 mock endpoints. There is no dedicated FamilyPortalService in the current microservice architecture. The endpoints aggregate data from:
careplan-service(care plan view)notification-service(messages, notifications)identity-service(family member auth, payments)
No FamilyPortalController exists in the Web BFF. Building a full family portal BFF layer during the mock-to-real migration would be a parallel new-feature build, not a migration task.
Decision
Family Portal routes are SCOPED OUT of the mock-to-real MVP migration.
The 16 family portal mock endpoints will remain served by the mock API fallback indefinitely until a dedicated post-MVP sprint is resourced.
Rationale
- Zero real service coverage — building from scratch, not migrating
- Requires new BFF controller + aggregation logic across 3+ services
- Family Portal is a secondary product — primary migration priority is Back Office and Mobile
- The mock API fallback handles family portal routes with zero user impact during migration
Post-MVP Implementation Plan
| Phase | Work | Timeline |
|---|---|---|
| FP-1 | Define FamilyPortalController in Web BFF; aggregate careplan + identity endpoints |
Sprint after MVP cutover |
| FP-2 | Wire notification-service for family inbox + message threads | FP-1 + 1 sprint |
| FP-3 | Payment initiation (Stripe/payment gateway) — separate investigation | TBD |
| FP-4 | Family Portal authentication (separate Entra B2C tenant for non-staff) | TBD — security review required |
Consequences
- Positive: Migration timeline stays at ~3 weeks; no scope creep
- Positive: Family Portal gets proper design attention post-MVP
- Negative: Mock data continues serving family portal users during MVP period
- Accepted risk: Mock data quality for family portal is sufficient for demo and pilot; real families not on-boarded until FP phases complete
Mock Fallback Route Config
# application.yml — BFF fallback config
mock-api:
fallback-routes:
- /api/v1/family/** # permanent until FP-1 sprint
DA-9. Demo Protection Protocol
Context
During Phase 3 cutovers, demo sessions may be running against staging or dev environments. A failed real service during a demo is unacceptable. This protocol defines demo-critical routes and a DEMO_MODE fallback concept.
Demo-Critical Routes
Routes that, if broken, will visibly fail a standard stakeholder demo:
| Route | Why Critical |
|---|---|
GET /api/v1/dashboard/stats |
First screen shown in every demo |
GET /api/v1/service-users |
Client list — primary navigation |
GET /api/v1/visits |
Visit calendar — core value demo |
GET /api/v1/care-plans |
Care plan list — clinical workflow demo |
GET /api/v1/assessments |
Assessment list — intake workflow demo |
GET /api/v1/notifications/inbox |
Notification bell — real-time demo |
GET /api/v1/incidents |
Incident log — compliance demo |
GET /api/v1/family/* |
Family portal views — stakeholder demo |
DEMO_MODE Flag
Add a DEMO_MODE environment variable to the BFF:
# application.yml
demo:
mode: ${DEMO_MODE:false}
protected-routes:
- /api/v1/dashboard/stats
- /api/v1/service-users
- /api/v1/visits
- /api/v1/care-plans
- /api/v1/assessments
- /api/v1/notifications/inbox
- /api/v1/incidents
- /api/v1/family/**
When DEMO_MODE=true:
- BFF always serves mock fallback for protected routes, regardless of whether the real service is available
- Real services continue running and accepting non-demo traffic
- A
X-Demo-Mode: trueresponse header is added to indicate source
When DEMO_MODE=false (default):
- Normal routing applies; real services used where wired; fallback only on error
Stability Window Protocol
For each demo-critical domain, the fallback is not disabled until 48 consecutive hours of stability in the target environment:
48h stability check:
[ ] Zero 5xx errors from real service in Azure Monitor
[ ] P95 latency < 500ms for the domain's endpoints
[ ] Zero fallback activations recorded in BFF logs
[ ] At least 1 full demo walkthrough completed successfully against real backend
Only after all 4 checks pass → disable mock fallback for this domain
Pre-Demo Checklist
Before any stakeholder demo during Phase 3:
# 1. Enable DEMO_MODE in dev/staging BFF
az containerapp update \
--name ca-vcc-dev-web-bff-001 \
--resource-group rg-vcc-dev-001 \
--set-env-vars DEMO_MODE=true
# 2. Verify mock fallback is serving demo-critical routes
curl -s -H "Authorization: Bearer $DEMO_JWT" \
https://<bff-fqdn>/api/v1/dashboard/stats \
| jq '.totalClients'
# Expected: non-null value from mock data
# 3. After demo — reset to real-service mode
az containerapp update \
--name ca-vcc-dev-web-bff-001 \
--resource-group rg-vcc-dev-001 \
--set-env-vars DEMO_MODE=false
Mock Data Preservation
The mock API's seed data (server.js in-memory arrays) must not be modified or reset during active demo periods. Tag a stable demo snapshot:
# Before Phase 3 begins, create a demo-data tag in git
cd /Users/makinja/projects/client/lumiscare/mock-api
git tag demo-data-snapshot-$(date +%Y%m%d)
If mock data gets corrupted, restore from this tag and restart the mock API container.
LumisCare Demo P0-Batch Closure — MC #102844
LumisCare Demo P0-Batch Closure — MC #102844
Date: 2026-06-03 Status: DONE — 9/9 P0 demo defects fixed, deployed, live-verified.
Scope
Frontend: GlobalSearch service-users endpoint, Assessments/Notifications/CQC spinner-stuck error handling, Snowit branding removal. Backend: visits 500, scheduling 500, incidents/assessments/training-matrix 404 routes, intake/careplan detail 500, careplan UUID seed.
Deploy
- Frontend: commit ceeb636d, live bundle index-BBJqymqC.js (https://app.lumiscare.com/ => HTTP/2 200).
- Backend: web-bff rev 0000036 + visits-service rev 0000010.
Validation (Proveo retest 2026-06-03T07:08Z — 9/9 PASS)
Proveo Retest Report — MC #102844
Date: 2026-06-03T07:08:57.476Z Bundle: index-BBJqymqC.js | BFF: rev 0000036 | Visits: rev 0000010
Summary: 9/9 fixed
| ID | Name | Verdict | Screenshot | Note |
|---|---|---|---|---|
| R01 | Global search → service-users endpoint | PASS | R01-search-typed.png | searchInput visible: true, result text found: true, service-users called: true, /clients called: false, /clients 404: fa |
| R02 | /assessments page renders | PASS | R02-assessments.png | noSpinner: true, hasSpinner: false, hasContent: true, isLogin: false, url: https://app.lumiscare.com/assessments |
| R03 | /notifications page renders | PASS | R03-notifications.png | noSpinner: true, hasSpinner: false, hasContent: true, isLogin: false |
| R04 | /cqc-readiness page renders | PASS | R04-cqc-readiness.png | noSpinner: true, hasSpinner: false, hasContent: true, is404: false, url: https://app.lumiscare.com/cqc-readiness |
| R05 | Login page footer — no Snowit/v2.0 branding | PASS | R05-login-footer.png | hasSnowit: false, hasV2Footer: false. Bundle scan: 0 hits for 'Powered by Snowit'. v2.0 strings in bundle are MSAL OAuth |
| R06 | /visits page renders (visits-service 200) | PASS | R06-visits.png | noSpinner: true, has500: false, has200: false, hasVisitContent: true, calls: [] |
| R07 | Scheduling page renders | PASS | R07-scheduling.png | landed: false, noSpinner: true, has500: false, has200: false, hasSchedContent: true, url: https://app.lumiscare.com/back |
| R08 | Incidents list renders (GET incidents 200) | PASS | R08-incidents.png | landed: false, noSpinner: true, has404: false, has200: false, hasIncidentContent: true, url: https://app.lumiscare.com/b |
| R09 | Care plan detail — no 500/white-screen | PASS | R09-careplan-detail.png | hasList: true, clickedDetail: true, has500: false, isWhiteScreen: false, hasDetailContent: true, url: https://app.lumisc |
Failed Items
Evidence
- Screenshots: /tmp/alai/lumiscare-finish/qa/retest/screenshots/
- Results JSON: /tmp/alai/lumiscare-finish/qa/retest/retest-results.json
P2P verifier
Company Mesh thread mesh-thr-bc26724d-4348-4bd9-ac5d-fb707942e536 → PASS.
Evidence
- /tmp/evidence-102844/ (verdict.json, RETEST-REPORT.md, retest-results.json, verification.json)
- Screenshots: /tmp/alai/lumiscare-finish/qa/retest/screenshots/
Backlog (not in this batch)
P1/P2 empty seeded data, family-portal app, finance dashboard zeros, careplan-service detail seed, intake/assessment-service deeper wiring; source-hygiene merge branch fix/demo-be-p0-102844; security debt MC #102747.