מטופל לוחץ על כפתור בשם “Disconnect My Data”. אפליקציית הווב מציגה וי (checkmark) ירוק ואישור עליז. אי שם בתור רקע, תהליך עובד (worker process) מתעורר לסנכרון הלילי שלו, שולף משימה שנוצרה אתמול, ומתחיל להזרים היסטוריית תרופות של שנתיים לאשכול אנליטי (analytics cluster) בשרשרת הנתונים. המשתמש בטח בממשק. המערכת בגדה באמון הזה.
מצב כשל ספציפי זה רודף ארכיטקטורות של נתוני בריאות מכיוון שהסיכונים כל כך גבוהים. הרשאה מיושנת (stale permission) אינה באג קטן; היא פריצה פעילה. הפתרון הוא לקשור כל בקשת נתונים בודדת ל"קבלה של הסכמה" (consent receipt): רשומה קטנה ומובנית הנושאת את כוונת המשתמש מהממשק (UI) ועד למנוע המדיניות (policy engine) שלכם, לעסקאות בבסיס הנתונים שלכם, ולכל תהליך עובד ברקע. היא לעולם אינה שומרת ערכים קליניים. היא שומרת רק את הזכות לגשת אליהם, עם חותמת גרסה שלא יכולה להשתנות בשקט מאחורי הקלעים.
מה הקבלה באמת מכילה
חשבו על הקבלה כעל חוזה מוגדר תחום (scoped contract), ולא כעל דגל של סשן (session flag). היא מכילה מזהה מתן הרשאה (grant identifier), את נושא הנתונים, את היקף הגישה המדויק (תוצאות מעבדה, סימנים חיוניים, היסטוריית תרופות), חלון תוקף מוגבל בזמן ומספר גרסה. כאשר צד-לקוח (frontend) מבקש גישה בשם משתמש, ה-API מנפיק את הקבלה הזו. ה-frontend מחזיק בה. כל שירות בשרשרת הנתונים (downstream service) שרוצה לקרוא נתוני בריאות חייב להציג את הקבלה לשכבת מדיניות מרכזית ולקבל אישור מפורש לפני פתיחת הרשומה.
זה חשוב מכיוון שמערכות בריאות טועות לעיתים קרובות ומתבלבלות בין טוקן (token) של חשבון משתמש לבין הסכמה. טוקן אומר מי אתם. קבלה אומרת מה מותר לכם לעשות ברגע זה. אם השניים מתרחקים זה מזה, הקבלה תמיד צריכה לנצח.
תנו גרסאות לכל דבר
בנו את מאגר ההסכמות שלכם כיומן "הוספה בלבד" (append-only log). כאשר משתמש מעניק לראשונה גישה לרשומות החיסונים שלו, זוהי גרסה אחת. אם מאוחר יותר הוא מצמצם את ההיקף כדי להחריג ספקים מסוימים, או אם הוא מבטל את ההרשאה לחלוטין, אל תדרוסו את הרשומה הראשונה. כתבו גרסה שנייה. הקבלה שבידי ה-worker עדיין מציינת גרסה אחת, ומנוע המדיניות יכול לראות בדיוק מה גרסה אחת אפשרה ושהיא הוחלפה בגרסה חדשה בנקודת זמן מסוימת.
אי-שינוי (immutability) זה הוא עמוד השדרה של הביקורת (audit) שלכם. שישה חודשים לאחר מכן, כאשר קצין ציות שואל מדוע משימת ETL מסוימת רצה ביום חמישי אחר הצהריים, תוכלו לעקוב אחר גרסת ההרשאה המדויקת שהמשימה נשאה ולהוכיח שהיא הייתה תקפה כשהמשימה החלה. אם תאחסנו הסכמה כדגל בוליאני (boolean flag) בודד בפרופיל המשתמש, תמחקו את ההיסטוריה הזו. תאבדו את היכולת להבחין בין "זה מעולם לא הותר" לבין "זה הותר כשהמשימה החלה, אך המשתמש שינה את דעתו שעתיים לאחר מכן".
תכננו נקודות קצה (Endpoints) לשקיפות
API של הסכמות צריך לחשוף נתיבים (routes) ברורים וספציפיים. תנו ל-POST /grants ליצור הרשאה חדשה. תנו ל-GET /grants/{id} להחזיר את המצב הנוכחי של קבלה ספציפית. תנו ל-POST /grants/{id}/revoke להתחיל תהליך ביטול. אל תעשו כאילו לחיצה על ביטול מוחקת באופן מיידי כל עותק של נתוני בריאות שצף בצינורות הנתונים (pipelines) שלכם. במקום זאת, החזירו מזהה פעולת ביטול עם סטטוס 202 Accepted. זה אומר למשתמש שהבקשה אמיתית, שהיא מתחילה, ושהוא יכול לעקוב אחריה.
מזהה הפעולה הזה הופך לקריטי כאשר המשתמש לוחץ פעמיים כי הממשק הרגיש איטי. אם הם שולחים בקשת ביטול שנייה, החזירו את מזהה הפעולה המקורי. אידמפוטנטיות (Idempotency) כאן אינה "בונוס נחמד"; היא מונעת פאניקה כפולה ומספקת למשתמש מקור אמת יחיד למצב הבקשה שלו.
בקשו רשות ברגע האחרון האפשרי
טעות נפוצה היא לבדוק הסכמה בשער ה-API (API gateway) ואז לסמוך על דגל שמור בזיכרון מטמון (cached flag) עמוק בתוך ה-worker. אל תעשו זאת. ה-worker צריך לשאת את הקבלה שלו לאורך כל מחזור חיי המשימה. רגע לפני שהוא מבצע את השאילתה מול מאגר רשומות הבריאות, הוא חייב לשאול את שכבת המדיניות: "האם גרסה שלוש של ההרשאה הספציפית הזו עדיין תקפה עבור ההיקף המדויק הזה?". אם התשובה היא לא, ה-worker עוצר. הוא מכשל את המשימה. הוא לא מנסה שוב.
לוגיקת ניסיונות חוזרים (retry logic) היא רעל כאן. חוסר התאמה בגרסה אינו תקלה רגעית ברשת. זוהי החלטה אנושית. המשתמש ביטל, או שההרשאה פגה, או שההיקף הצטמצם. אם תנסו שוב שלוש פעמים ותצליחו בפעם הרביעית בגלל תנאי מרוץ (race condition), בדיוק הפרתם הסכמה. התייחסו לחוסר ההתאמה כאל כישלון מוחלט (hard failure), העבירו אותו לתור ה-dead-letter queue או ללוח הבקרה התפעולי שלכם, ותנו לאדם לחקור זאת.
התמודדו עם התרחישים המורכבים
מערכות אמיתיות אינן פועלות בשלבים מסודרים. משתמשים משאירים לשוניות דפדפן ישנות פתוחות. ייבואים מרוכזים (Bulk imports) רצים במשך עשרים דקות. טווחים (Scopes) משתנים בזמן שסנכרון נמצא באמצע הדרך. ה-consent API שלך זקוק לכללים מפורשים לרגעים הללו.
לשוניות דפדפן שאינן מעודכנות. משתמש מבטל גישה בלשונית שנפתחה זה עתה. לשונית ישנה יותר, שעדיין מחזיקה באובייקט הרשאה (grant object) מטעינת דף קודמת, מנסה להתחבר מחדש. ה-backend שלך חייב לדחות את הקבלה (receipt) הישנה הזו באופן מיידי ולכפות בדיקת הסכמה (consent review) חדשה. הרשאה שבוטלה צריכה להתנהג כמו דרכון שבוטל: היא לא תחזור לחיים רק כי המחזיק מצא עותק ישן במגירה.
ייבואים בתהליך (In-flight imports). אם ייבוא מרוכז רץ והמשתמש מבטל את ההרשאה, עליך לדאוג לשני דברים שיקרו בו-זמנית. ראשית, הפסק לאפשר כתיבות חדשות ברגע שהגרסה נדחית. שנית, הצג למשתמש התקדמות ניקוי אמיתית באמצעות ה-operation ID. ספק להם דף סטטוס אמין: "הביטול התקבל. שמונה-עשרה כתיבות ממתינות ונמצאות בתהליך מחיקה". אל תאפשר ל-workers לבצע commit לנתונים באמצעות גרסת הרשאה שכבר סומנה כלא תקפה.
שינויי טווח (Scope changes). נניח שמשתמש העניק במקור גישה לחמש שנות היסטוריה ומאוחר יותר התאים אותה לשישה חודשים. אל תשנה (mutate) את ההרשאה המקורית. סגור את גרסה אחת, הנפק את גרסה שתיים עם חלון הזמן המצומצם יותר, וכפה על כל התהליכים המתמשכים להתאים את עצמם לגבולות החדשים. הגרסה הישנה נשארת בלוג שלך כעובדה היסטורית, ולא כהרשאה פעילה.
כללי אבטחה מחמירים
הקבלה (Receipt) היא רגישה כשלעצמה, אך היא אינה נתון קליני. שמור עליה מופרדת בארכיטקטורה שלך. רק המטופל או תפקיד מוסמך ספציפי — כגון אפוטרופוס חוקי או מטפל מורשה — אמורים להיות מסוגלים לצפות בקבלה או לבטל אותה. אכיף זאת בשכבת הנתונים (data layer), ולא רק בטבלת ניתוב ה-UI.
לוגי השגיאות שלך ינסו "לשאוב" פנימה נתונים בריאותיים כאשר משימות נכשלות. נאבק בנטייה הזו באגרסיביות. כאשר worker קורס מכיוון שהציג קבלת הסכמה לא תקפה, רשום בלוג את ה-grant ID, את הגרסה ואת השגיאה. לעולם אל תרשום את מזהה המטופל, את קוד האבחנה או את ערך המעבדה שה-worker ניסה לשלוף. נתונים בריאותיים בלוגים מתפשטים כמו עובש: הם מגובים, מאונדקסים ונשכחים בדרכים שעוקפות את בקרות הגישה הרגילות שלך.
לבסוף, לעולם אל תשחזר הרשאה פעילה ישנה במהלך שחזור מערכת. אם אתה מבצע rollback למסד נתונים או משחזר snapshot במקרה שמכיל גרסה של טבלת הרשאות שקדמה לביטול, ה-runbook שלך חייב להשבית באופן אוטומטי את ההרשאות המשוחזרות הללו לפני שהשירות מקבל תעבורה חדשה. מצבי הסכמה היסטוריים שייכים ללוג הביקורת (audit log), לעולם לא לסט של הכללים הפעילים.
