Refactor Sequence

The ordered build order for rolling out the Canvas-first architecture. Foundational services first, then services that depend on them.

Note on page history. This page previously tracked the Bask → Canvas migration, which completed 2026-03-05 (see the historical note below if you need context). Under the Canvas-first redesign, the live docs topic for this URL is now the refactor sequence — the order in which services are built, slimmed, extracted, or retired to reach the Canvas-first target state described in Architecture Overview.

Philosophy

Every refactor step follows the same pattern:

  1. Red-line the domain spec (lives in Architecture or its subpages).
  2. Plan the migration — what moves where, what's deleted, what's dual-written during cut-over.
  3. Implement in the new service.
  4. Cut over with event subscribers + feature flag if needed.
  5. E2E tests verify no regressions.
  6. Update docs to match what was actually built.

Steps run in dependency order below. Phases 1–2 are foundational; nothing else can land until they do. Phase 14 (retire canvas-pioneer-integration-service) can only happen once every responsibility it owns has been redistributed.

17-Step Refactor Plan

1
Organization Service
Provision DB + core entities (organizations, organization_branding, organization_portal_config, organization_notification_config, organization_custom_domains, admin_users, admin_org_memberships, physician_org_memberships). Bootstrap YourEra DTC + existing GuideGLP clients as organizations rows with Canvas Organization mirrors.
Dependencies: None (foundational)
2
@yourera/canvas-client (shared library)
Extract Canvas OAuth + FHIR HTTP helpers from canvas-pioneer-integration-service into a reusable npm package. Typed resource wrappers, retries, pagination. Every downstream service depends on this library.
Dependencies: None (library)
3
Patient Service (slim)
Rebuild from v0 monolith to two tables: patient_directory (MRN ↔ canvas_patient_id ↔ stripe_customer_id ↔ clerk_id ↔ organization_id) + INSERT-only intake_snapshots. Migrate existing patient records. Drop clinical columns. Add POST /patients/email-lookup for intake duplicate detection.
Dependencies: Organization Service (Phase 1), canvas-client (Phase 2)
4
Webhook Receiver
Stand up a single public Canvas webhook endpoint. Per-Canvas-webhook signature verification. Normalization + fan-out via SQS/EventBridge to internal consumers (orchestrator, refill-scheduler, patient-service, etc.).
Dependencies: canvas-client (Phase 2)
5
Intake Gateway (Canvas-atomic)
Rewrite intake completion to atomically write Canvas resources (Patient, Observation, Condition, AllergyIntolerance, QuestionnaireResponse, Consent, DocumentReference) + patient-service rows in one logical transaction with saga compensation. Add persistent intake_sessions for resume + lead capture. Emit contact.captured, intake.progressed, intake.abandoned, intake.disqualified, intake.completed.
Dependencies: Phases 1–4
6
Canvas Plugins (org-aware)
Update prescription_queue, portal_invite, and other SDK plugins to respect managingOrganization for routing and branding. Use CPA (Canvas Plugin Assistant) — Canvas's own Claude Code plugin for development. See Canvas Plugins.
Dependencies: Organization Service (Phase 1)
7
Refill Scheduler
Build materialized-view service. Webhook ingest + cadence worker (runs every minute) + nightly safety-net reconciler that diffs the view against Canvas to catch webhook drops. Emits refill.upcoming, refill.due, refill.overdue. Writes zero Canvas resources.
Dependencies: Phases 2, 4
8
Pharmacy Router (GMP + Strive, Boothwyn removed)
Remove Boothwyn integration. Build GMP driver (PioneerRx backend); build Strive driver. Seed pharmacy_routes with GMP's state-license matrix. Live routing re-evaluation per saga (ops updates routes as GMP adds licenses; refills automatically migrate).
Dependencies: Phase 2
9
Prescription Orchestrator (slim saga)
Strip canvas-pioneer-era scope. Remove Canvas auth, patient records, Stripe, pharmacy HTTP, email sending. Keep only saga state + state machine coordinating preflight → payment → pharmacy → shipping → notification. Implement preflight (allergy, clinical_hold, patient.active, org.suspended). Prepaid orders skip pending_payment per commerce integration.
Dependencies: Phases 2, 7, 8
10
Shipping Service (Canvas-address, FedEx 2Day)
Drop cached patient demographics. Fetch shipping address from Canvas live at label time. Default to FedEx 2Day Express (Ground is retired from our flows). Pharmacy-ships (Mode B) for tracking-only shipments where pharmacy labels directly. Pharmacy handles cold-chain packaging (no software involvement).
Dependencies: Phases 2, 9
11
Notification Service (expanded)
Expand from delivery-only to full outbound-comms + CRM. New tables: contacts, campaigns, campaign_enrollments, messaging_preferences. Per-org verified senders (SES + Twilio). HIPAA-aware category model (transactional / clinical / marketing). Event-triggered drips + scheduled campaigns + one-off blasts. HubSpot retires.
Dependencies: Phases 1, 3, 4
12
Payment Service (extract from canvas-pioneer)
New service. Stripe customers, PaymentMethods, SetupIntents, PaymentIntents, Subscriptions (Stripe-side), Refunds, Disputes. Idempotent everywhere (deterministic keys from our entity IDs). Zero clinical data; statement descriptors generic per org. Stripe webhook endpoint with signature verification.
Dependencies: Phases 1, 3
13
Commerce Service
New service. Orders as correlation keys (joining Stripe PI + Canvas MR + shipment + coupon + saga). Business-level subscriptions (distinct from Stripe Subscription). Catalog (products + SKUs + billing plans). Coupons. Prepaid bundled plans (quarterly, 6-month) charged fully upfront; subsequent fills within paid period skip new charges. Pay-as-you-go monthly charges per fill.
Dependencies: Phases 3, 9, 12
14
canvas-pioneer-integration-service retirement
Delete the service. By this point every responsibility has been redistributed: Canvas OAuth → canvas-client library; webhook handling → webhook-receiver; patient records → Canvas + patient-service; Stripe glue → payment-service; welcome emails → notification-service; pharmacy HTTP → pharmacy-router. No code remains to port.
Dependencies: Phases 2, 3, 7, 8, 9, 11, 12
15
Admin Portal (tenant-scoped + RBAC)
Rebuild with RBAC: superadmin / org_admin / org_user / support. Dual auth (Clerk for internal, OTP+JWT for B2B org admins). Org membership resolution + tenant scoping at middleware. Superadmin impersonate-org view pivot. Saga operations console (retry, reassign pharmacy, cancel). Campaign authoring UI. Full audit logging on every write.
Dependencies: Phases 1–13
16
Patient Portal (white-label)
Rebuild for hostname → org resolution (custom domains + default subdomain fallback). Per-org theming from organization_branding. Clerk auth with org-scoped sessions. Live canvas-client reads for clinical data. Patient-initiated cancel (cancel_at_period_end), pause, payment method update.
Dependencies: Phases 1–13
17
GuideGLP Migration
Retire the guide-glp standalone service + DB. Migrate each GuideGLP client's patients into the shared Canvas instance as Patient.managingOrganization = {their_canvas_org_id}. Their orders and subscriptions now flow through the unified orchestrator + core services. Their branded portal is served from the shared patient-portal via custom domain.
Dependencies: All of Phases 1–16

Verification after each phase

  • All existing tests still pass (no regressions).
  • New service passes independently.
  • End-to-end pipeline works against a test Canvas instance.
  • Tenant scoping verified: cross-org queries return empty for non-superadmin callers.
  • Docs updated on this site; phase marked complete.
  • Health check returns 200 on the new ALB path.

Historical: Bask → Canvas cutover (completed 2026-03-05)

Before the Canvas-first redesign, YourEra migrated from Bask EMR to Canvas Medical. That migration is done. Key markers for historical context:

  • intake.yourera.com is BASK (never deploy to it); intake.hisera.com is Canvas.
  • Ghost sub-domains (portal.yourera.com, admin.yourera.com) were moved to BASK as part of the cutover.
  • admin.hisera.com became the Canvas-side admin portal.
  • Canvas plugin prescription_queue v1.3.4 deployed with admin.hisera.com URLs.

The full Bask migration runbook is preserved in the repo at yourera_to_hisera/MIGRATION-RUNBOOK.md. If you need to understand historical domain ownership or DNS state, consult that file. It is not part of the live Canvas-first architecture.