患者が「データの連携を解除する」というラベルのボタンをクリックします。ウェブアプリは緑色のチェックマークを表示し、明るい確認メッセージを出します。しかし、バックグラウンドのキューのどこかで、夜間の同期のために起動したワーカープロセスが、昨日作成されたジョブを取り出し、2年分の投薬履歴を下流の分析クラスターへストリーミングし始めます。ユーザーはインターフェースを信頼していました。システムはその信頼を裏切ったのです。
この特定の失敗モードは、リスクが非常に高いため、ヘルスケアデータのアーキテクチャにおいて常に課題となります。古い権限は単なる軽微なバグではなく、アクティブな侵害です。解決策は、すべてのデータリクエストを「同意レシート(consent receipt)」に紐付けることです。これは、UIからポリシーエンジン、データベーストランザクション、そしてすべてのバックグラウンドワーカーに至るまで、ユーザーの意図を運ぶ、構造化された小さなレコードです。これには臨床的な値は一切保存されません。それらにアクセスする権利のみを保存し、裏側で密かに変更されることのないバージョン番号を付与します。
同意レシートが実際に保持するもの
同意レシートを、セッションフラグではなく、スコープの限定された契約(scoped contract)と考えてください。これには、付与識別子(grant identifier)、データ主体、アクセス権限の正確なスコープ(検査結果、バイタル、投薬履歴)、有効期間、およびバージョン番号が含まれます。フロントエンドがユーザーに代わってアクセスをリクエストすると、APIはこのレシートを発行します。フロントエンドはそれを保持します。ヘルスケアデータを読み取りたいすべてのダウンストリームサービスは、中央のポリシーレイヤーにレシートを提示し、レコードを開く前に明示的な承認を得なければなりません。
これが重要なのは、ヘルスケアシステムにおいて、ユーザーアカウントのトークンを同意と混同してしまうことがよくあるからです。トークンは「あなたが誰であるか」を示します。レシートは「あなたが今、何をすることを許可されているか」を示します。両者に乖離が生じた場合、常にレシートが優先されるべきです。
すべてをバージョン管理する
同意ストアは、追加専用ログ(append-only log)として構築してください。ユーザーが最初に予防接種記録へのアクセスを許可したとき、それがバージョン1になります。その後、特定のプロバイダーを除外するようにスコープを狭めたり、完全に撤回したりした場合でも、最初のエントリを上書きしてはいけません。バージョン2を書き込んでください。ワーカーが持っているレシートは依然としてバージョン1を示しており、ポリシーエンジンはバージョン1が何を許可していたのか、そしてそれが特定のタイムスタンプで上書きされたのかを正確に把握できます。
この不変性(immutability)こそが、監査のバックボーンとなります。6ヶ月後、コンプライアンス担当者が「なぜ特定のETLジョブが木曜日の午後に実行されたのか」と尋ねたとき、そのジョブが保持していた正確な付与バージョンを追跡し、ジョブ開始時にそれが有効であったことを証明できます。同意をユーザープロファイル内の単一のブール値フラグとして保存してしまうと、その履歴は消えてしまいます。「これは一度も許可されていなかった」のか、「ジョブ開始時には許可されていたが、2時間後にユーザーが考えを変えた」のかを区別する能力を失ってしまうのです。
正直な設計のエンドポイント
同意APIは、明確で具体的なルートを公開すべきです。POST /grants で新しい権限を作成し、GET /grants/{id} で特定のレシートの現在の状態を返し、POST /grants/{id}/revoke で撤回を開始させます。撤回を実行した瞬間に、パイプライン内を流れるヘルスケアデータのすべてのコピーが即座に消去されるかのように振る舞ってはいけません。代わりに、202 Accepted ステータスとともに撤回操作ID(operation ID)を返します。これにより、ユーザーに対してリクエストが受理され、処理が開始されており、進捗を追跡できることを伝えます。
インターフェースの反応が遅いと感じてユーザーがボタンをダブルクリックした場合、その操作IDが極めて重要になります。もしユーザーが2回目の撤回リクエストを送信した場合は、元の操作IDを返してください。ここでのべき等性(idempotency)は、単なる「あれば便利な機能」ではありません。重複によるパニックを防ぎ、ユーザーにリクエストのステータスに関する単一の真実のソース(single source of truth)を提供するためのものです。
可能な限り最後の瞬間に許可を求める
よくある間違いは、APIゲートウェイで同意を確認し、その後ワーカーの深い部分にあるキャッシュされたフラグを信頼してしまうことです。このようなことはしないでください。ワーカーは、ジョブのライフサイクルを通じてレシートを保持すべきです。ヘルスケア記録ストアに対してクエリを実行する直前に、ポリシーレイヤーに対して「この特定の付与のバージョン3は、この正確なスコープに対してまだ有効か?」と問い合わせる必要があります。もし答えが「いいえ」であれば、ワーカーは停止します。ジョブを失敗させます。リトライはしません。
ここでのリトライロジックは毒となります。バージョンの不一致は、ネットワークの一時的な瞬断ではありません。それは人間の決定です。ユーザーが撤回したか、付与の期限が切れたか、あるいはスコープが縮小したのです。もし3回リトライして、競合状態(race condition)によって4回目で成功してしまったとしたら、あなたは同意に違反したことになります。不一致は致命的な失敗として扱い、デッドレターキューや運用ダッシュボードに通知し、人間が調査できるようにしてください。
厄介なシナリオへの対処
実際のシステムは、整然としたステップで進むわけではありません。ユーザーは古いブラウザのタブを開いたままにしたり、一括インポートが20分間続いたりします。同期が半分終わったところでスコープが変更されることもあります。あなたの同意APIには、こうした瞬間に対応するための明確なルールが必要です。
古いブラウザのタブ。 ユーザーが新しく開いたタブでアクセス権を取り消します。しかし、以前のページロード時の許可オブジェクトを保持したままの古いタブが、再接続を試みます。バックエンドは、その古いレシートを即座に拒否し、改めて同意の確認を強制しなければなりません。取り消された許可は、失効したパスポートのように振る舞うべきです。保持者が引き出しから古いコピーを見つけたからといって、それが生き返ることはありません。
実行中のインポート。 一括インポートが実行されている間にユーザーが権限を取り消した場合、同時に2つのことが起こる必要があります。第一に、バージョンが拒否された瞬間に、新しい書き込みを許可しないこと。第二に、操作IDを通じて、ユーザーに実際のクリーンアップの進捗を表示することです。「取り消しが受理されました。保留中の18件の書き込みを削除しています」といった、真実に基づいたステータス画面を提供してください。すでに無効とマークされた許可バージョンを使用して、ワーカーがデータをコミットすることを許してはいけません。
スコープの変更。 例えば、ユーザーが当初は5年分の履歴へのアクセスを許可し、後にそれを6ヶ月間に調整したとします。元の許可を書き換えてはいけません。バージョン1を閉じ、より狭い期間を持つバージョン2を発行し、進行中のすべてのプロセスに対して新しい境界線との整合性を取るよう強制してください。古いバージョンは、有効な権限としてではなく、歴史的事実としてログに残すべきです。
厳格なセキュリティルール
レシート自体は機密情報ですが、臨床データそのものではありません。アーキテクチャ内ではこれらを分離して管理してください。患者本人、または法的保護者や認定ケアギバーといった、特別に委任された役割を持つ者のみが、レシートの閲覧や取り消しを行えるようにすべきです。これを単なるUIのルーティングテーブルではなく、データレイヤーで強制してください。
ジョブが失敗した際、エラーログがヘルスデータを吸い込もうとすることがあります。この傾向には積極的に抗ってください。無効な同意レシートを提示したためにワーカーが停止した場合、許可ID、バージョン、およびエラーをログに記録してください。ワーカーが取得しようとしていた患者識別子、診断コード、または検査値を決してログに記録してはいけません。ログ内のヘルスデータはカビのように広がります。バックアップされ、インデックス化され、通常のアクセス制御を回避するような形で忘れ去られてしまうのです。
最後に、システム復旧中に古い有効な許可を復元してはいけません。データベースをロールバックしたり、たまたま取り消し前の許可テーブルのバージョンを含むスナップショットを復元したりした場合、サービスが新しいトラフィックを受け入れる前に、ランブックによってそれらの復活した権限を自動的に無効化しなければなりません。過去の同意状態は監査ログに属するものであり、アクティブなルールセットに属するものではありません。
