-
Notifications
You must be signed in to change notification settings - Fork 1
Add data-access-strategy skill: SDK vs FHIR, existing vs custom data #93
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
beaugunderson
wants to merge
2
commits into
main
Choose a base branch
from
bg-data-access-strategy
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
107 changes: 107 additions & 0 deletions
107
canvas-plugin-assistant/skills/data-access-strategy/SKILL.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,107 @@ | ||
| --- | ||
| name: data-access-strategy | ||
| description: Decide how a Canvas plugin reads and writes data - SDK vs FHIR API, and existing Canvas data models vs Custom Data. Use whenever a plugin needs to read or write clinical/operational data, before writing the spec or any data-access code. | ||
| --- | ||
|
|
||
| # Data Access Strategy | ||
|
|
||
| This skill decides **how** a Canvas plugin touches data. There are two questions, and the plugin spec must answer both with a recorded rationale: | ||
|
|
||
| 1. **SDK or FHIR API?** — for every read and every write the plugin performs. | ||
| 2. **Existing Canvas data models or Custom Data?** — for everything the plugin needs to persist. | ||
|
|
||
| The defaults are the same on both axes: **stay inside the SDK and reuse what Canvas already models.** Escalate to FHIR or to Custom Data only when the default genuinely can't do the job, and write down why. | ||
|
|
||
| ## When to Use This Skill | ||
|
|
||
| Invoke this skill **before writing the spec**, as soon as you know what data the plugin reads or writes. Also use it when: | ||
|
|
||
| - Mapping requirements to a `Data Requirements` / `Data Access Strategy` spec section | ||
| - About to write a data-model query, an Effect, or a FHIR call | ||
| - Reviewing a plugin and questioning whether a FHIR call or a CustomModel was the right call | ||
|
|
||
| ## Decision 1: SDK vs FHIR API | ||
|
|
||
| **Default to the SDK. Use FHIR only when the SDK cannot accomplish the task.** | ||
|
|
||
| Why the SDK is the default: | ||
|
|
||
| - Runs in-process, no network round-trip, no token to manage or leak. | ||
| - No OAuth scopes to request, store, or scope to a patient. | ||
| - Reads come straight from the data module (Django ORM over read-only views); writes go through Effects, which Canvas validates and applies. | ||
| - Less code, fewer failure modes, nothing to rotate. | ||
|
|
||
| ### Reads | ||
|
|
||
| - **SDK first.** If the data you need is exposed in the data module (`canvas_sdk.v1.data`), read it there. This covers the large majority of clinical and operational models. | ||
| - **FHIR only if** the data you need is **not** exposed in the SDK data module, or you need a FHIR-shaped resource bundle (e.g. an external integration expects FHIR JSON). | ||
|
|
||
| ### Writes | ||
|
|
||
| The SDK data models are **read-only**. Mutations happen one of two ways, and the only question is whether an Effect exists for the write you need: | ||
|
|
||
| - **SDK Effects** — if there is an Effect for the change you want (e.g. `AddBannerAlert`, `AddTask`, command effects, chart writes), use it. This is the default for writes. Effects are **not** tied to event handlers: a `BaseHandler` returns them from `compute()`, a `CronTask` returns them from `execute()` (`-> list[Effect]`), and a `SimpleAPI` route returns them from its method (`-> list[Response | Effect]`). So a scheduled job or an API endpoint can write via Effects just like an event handler. | ||
| - **FHIR API** — use it to create or update data when there is **no Effect** for the write you need. (A handful of effects are contextual to the in-flight command — e.g. `*__PRE_COMMIT` overrides — and only make sense from the handler responding to that event; those are command overrides, not general data writes.) | ||
|
|
||
| ### Quick reference | ||
|
|
||
| | Situation | Use | | ||
| |-----------|-----| | ||
| | Read a model exposed in `canvas_sdk.v1.data` | **SDK data module** | | ||
| | Read data not exposed in the SDK | **FHIR** | | ||
| | Write and an Effect exists for it | **SDK Effect** (works from `BaseHandler`, `CronTask`, or `SimpleAPI`) | | ||
| | Write with no matching Effect | **FHIR** | | ||
| | Plugin is itself a FHIR-shaped integration with an external system | **FHIR** | | ||
|
|
||
| When in doubt, check the **canvas-sdk** skill for whether a data model or Effect exists before reaching for FHIR. Most "we need FHIR" instincts are covered by an existing model or Effect. | ||
|
|
||
| ### If you choose FHIR | ||
|
|
||
| Reads/writes via FHIR carry security obligations the SDK does not. Invoke the **fhir-api-client-security** skill and confirm: | ||
|
|
||
| - token stored in a secret, never hardcoded | ||
| - patient-facing flows use a patient-scoped token | ||
| - minimum-necessary scopes requested | ||
|
|
||
| ## Decision 2: Existing Data Models vs Custom Data | ||
|
|
||
| **Default to existing Canvas data models. Add Custom Data only when no Canvas model represents the data.** | ||
|
|
||
| - **Use existing models** (via the SDK data module / Effects, or FHIR) whenever the data is a clinical or operational concept Canvas already knows about — patients, conditions, medications, tasks, appointments, notes, etc. Do not shadow a Canvas concept with your own table. | ||
| - **Use Custom Data** (`CustomModel` / `AttributeHub`) only for data that genuinely has **no home in Canvas**: | ||
| - plugin-specific configuration or state | ||
| - mappings to an external system's identifiers | ||
| - bookkeeping the plugin needs that Canvas does not model | ||
|
|
||
| Anti-pattern: storing a copy of patient/clinical data in a CustomModel "for convenience." Reference the Canvas record instead. If you do introduce any `CustomModel` or `AttributeHub`, invoke the **custom-data-patterns** skill before writing the model definitions. | ||
|
|
||
| ## Record the Rationale in the Spec | ||
|
|
||
| Every plugin spec must include a **Data Access Strategy** section that states the choices and *why*, so a reviewer can see the reasoning without reading the code. Keep it short — one line per decision: | ||
|
|
||
| ```markdown | ||
| ## Data Access Strategy | ||
|
|
||
| - **Reads**: SDK data module (`Patient`, `Observation`) — both exposed in `canvas_sdk.v1.data`. | ||
| - **Writes**: SDK Effect `AddBannerAlert` — fires in response to `VITALS_COMMAND__POST_COMMIT`, effect exists. | ||
| - **FHIR used**: No — SDK covers all reads and writes. | ||
| - **Custom Data**: No — all data maps to existing Canvas models. | ||
| ``` | ||
|
|
||
| When the plugin *does* escalate, the rationale must say what the SDK couldn't do: | ||
|
|
||
| ```markdown | ||
| ## Data Access Strategy | ||
|
|
||
| - **Reads**: SDK data module for patient + appointments. | ||
| - **Writes**: FHIR `DocumentReference` POST — no SDK Effect exists for creating documents. | ||
| - **FHIR used**: Yes, write only, for document creation; patient-scoped token, stored in secret. | ||
| - **Custom Data**: Yes — `ExternalSyncRecord` CustomModel maps Canvas appointments to the partner system's booking IDs (no Canvas model for this). custom-data-patterns skill applied. | ||
| ``` | ||
|
|
||
| ## Related Skills | ||
|
|
||
| - **canvas-sdk**: confirm whether a data model or Effect exists before choosing FHIR (preferred path). | ||
| - **fhir-api-client-development**: how to call FHIR once you've decided the SDK can't do it. | ||
| - **fhir-api-client-security**: required security review whenever FHIR is used. | ||
| - **custom-data-patterns**: required whenever a CustomModel or AttributeHub is introduced. |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the case that FHIR is the only option, it would be hugely valuable for folks in Eng and Product to know that the decision has been made, so that we can prioritize adding the capability to the SDK. FHIR calls from plugins should be avoided. I don't know how to do this though. Can CPA open a ticket in the repo maybe?