في أبريل 2020، أصبح كل startup صحي في ألمانيا مهماً فجأةً. الصيدليات كانت مثقلة بالطلبات. الأطباء يجرون مشاورات مرئية للمرة الأولى. المرضى يرسلون وصفات ورقية بالبريد. استيقظ البلد كله وأدرك أن مسار الوصفة الطبية - من لحظة كتابة الطبيب للدواء إلى حصول المريض عليه - كان يسير بشكل شبه كامل بالطريقة التقليدية. في هامبورغ، انضممت إلى meinrezept.online للمساعدة في بناء الـbackend الذي سيجلس في قلب كل هذا.

هذه ليست تدوينة “أنقذنا أرواحاً”. إنها تدوينة “هكذا بدت الهندسة الفعلية”، لأن الهندسة كانت ممتعة فعلاً وكثير منها غير مرئي من الخارج.

مشكلة تنسيق الوصفات

قبل أن تتمكن من توجيه وصفة طبية إلى صيدلية، يجب أن تتمكن من قراءتها. في ألمانيا عام 2020، كان هذا يعني التعامل مع تنسيقات متعددة: نموذج Muster-16 الورقي، تنسيقات الوصفات الرقمية المبكرة التي لم تُوحَّد بعد، صور الفاكس، وأحياناً PDF أنشأه مكتب الطبيب من أي نظام إدارة عيادة كان يشغّله.

لكل من هذه مسار استخراج مختلف. النموذج الورقي له تخطيط معروف لكنه يأتي كمسح ضوئي. التنسيقات الرقمية لها بيانات مُهيكلة لكن ليس كل الصيدليات تستطيع استهلاكها. الـPDF من نظام إدارة العيادة هو أسوأ الحالات: بيانات مُهيكلة محبوسة داخل تنسيق مستند لم يُصمَّم للتحليل الآلي أصلاً.

مهمة الـbackend كانت تطبيع كل هذه في تمثيل داخلي واحد يستطيع بقية النظام - طبقة توجيه الصيدليات، خدمة تتبع الطلبات، خدمة إشعار المرضى - استهلاكه دون الاهتمام بتنسيق الإدخال. هذه مشكلة صعبة بشكل لافت حين تتطور تنسيقات الإدخال تحتك في الوقت الفعلي، وهذا بالضبط ما كان يحدث عام 2020 بينما كانت الحكومة الألمانية تدفع بنشر الوصفة الإلكترونية (Elektronisches Rezept أو eRezept).

تكامل الصيدليات ثقيل بالإرث بحكم التصميم

هكذا تبدو فضاء تكامل الصيدليات من ناحية الـbackend: كل سلسلة صيدليات كبرى لها نظام طلب خاص بها، وكثير من الصيدليات المستقلة تشغّل WINAPO أو ProFit أو ADG - أنظمة إدارة عيادات عمرها عقود تعرض واجهات الطلب عبر بروتوكولات لن تسمع بها في سياق الـbackend الحديث. HTTP/JSON ليس تنسيق الـwire المسيطر. EDIFACT وأشكال HL7 تظهر. بعض الصيدليات لديها بوابة ويب وAPI غير رسمي هندسه أحدهم بعكس الهندسة. وبعضها لا يملك شيئاً وأنت عائد لإرسال بريد إلكتروني مُهيكل.

هذه ليست شكوى. هذا هو ما يبدو عليه تكامل الرعاية الصحية من الطرف الموجّه للمستهلك. عمل الـEHR على مستوى المستشفيات الذي أجريته في أماكن أخرى (AFAQ، HAKEEM) يعمل على مستوى تجريد مختلف - أنت تعمل مع HL7 FHIR، ولديك محركات تكامل مخصصة، ولديك نظير تقني في الصيدلية أو المختبر. على مستوى تطبيق الصيدلية للمستهلك، في الغالب تعمل مع ما تملكه الصيدلية فعلاً، وهو كثيراً ما يكون أقل بكثير مما ستختار.

RabbitMQ كان وسيط الرسائل هنا، وهذا منطقي لهذا المستوى. لم نكن نعمل بحجم Kafka؛ كنا نعمل بتسليم موثوق بين عدد قابل للإدارة من الخدمات مع دلالات إعادة المحاولة، ومعالجة الرسائل الميتة، والقدرة على فصل استلام الطلب الوارد عن إرساله للصيدلية. RabbitMQ يتعامل مع هذا بنظافة. طبولوجيا الـexchange - topic exchanges يوجّه بحسب منطقة الصيدلية ونوع الوصفة ومسار التنفيذ - سمحت لنا بإضافة منطق توجيه دون لمس كل مستهلك.

microservices بثلاث لغات وستة أسابيع

الـstack كان PHP 7 وPython وNode.js. هذا هو العالم الحقيقي. ترث اللغة التي كُتبت بها خدمتك الأولى، تختار الأداة الأنسب للمشكلة التالية (Python لعمل الاستخراج، Node.js لـAPI الحالة في الوقت الفعلي)، وتنتهي بقاعدة كود متعددة اللغات تُربطها عقود الرسائل بين الخدمات لا اتساق اللغة.

انضباط عقود الرسائل أهم من اتساق اللغة، بصراحة. إذا اتفقت كل خدمة على شكل حدث order.received - الـschema، الإصدار، الحقول المطلوبة - فحقيقة أن المنتج PHP والمستهلك Node.js تصبح تفصيلة تنفيذية. نمط الفشل الخطير هو السماح لكل خدمة بتطوير فهمها للدومين بشكل مستقل، بحيث يعني order.received شيئاً مختلفاً قليلاً لخدمة إرسال الصيدلية عما يعنيه لخدمة إشعار المريض. تكتشف هذا في أسوأ وقت ممكن: حين يتصل مريض بالدعم قائلاً إنه حصل على إشعار “تم إرسال الوصفة” لكن الصيدلية تقول إنها لا تجد سجلاً للطلب.

ReactJS في الـfrontend، الذي لم أكن مسؤولاً عنه بشكل أساسي، لكنه كان يتصل بنفس نموذج الدومين عبر API الحالة. الاتساق بين ما يراه المريض في الواجهة وما يقوله سجل أحداث الـbackend هو اهتمام تكامل مُقلَّل القيمة، وأصبح مرئياً جداً حين كشف حجم الطلبات في زمن الجائحة عن فجوات توقيت لم نختبرها على نطاق واسع.

الجدول الزمني للجائحة ضخّم كل شيء

الانضمام في أبريل 2020 كان انضماماً لفريق يجد فيه المنتج نفسه مُختبَراً بالطلب الحقيقي للمستخدم بطريقة لم يتوقعها أي بيئة staging. القلق الصحي محرّك حركة حقيقي. تتجمع الطلبات الواردة حول أي خبر - إعلان إغلاق، تغيير في ساعات عمل الصيدليات، تقرير عن نقص في دواء. تتعلم بسرعة كبيرة أي أجزاء خدمتك ذات حالة وأيها بلا حالة، لأن الأجزاء ذات الحالة هي التي تفشل بشكل غير أنيق حين ترتفع الأحمال.

ما علّمني هذا بشكل ملموس جداً: كانت خطوط الاستخراج بحاجة للانفصال عن مسار قبول الطلب. التكامل البطيء مع الصيدلية لا يجب أن يمنع استلام وصفة المريض والإقرار بها. الإقرار والتنفيذ اهتمامان مختلفان ويجب أن يفشلا بشكل مستقل. أجرينا هذا التغيير تحت الحمل الفعلي، وهو ليس مثالياً، لكنه ذاكرة هندسية حقيقية.

المدى عبر مجالات الرعاية الصحية

شحنت برمجيات رعاية صحية على مستويين مختلفين تماماً: أنظمة EHR المؤسسية الكبيرة وسير العمل السريرية (على مستوى المستشفيات، مئات الآلاف من سجلات المرضى، دعم القرار السريري المعقد)، والتطبيقات الصحية الموجّهة للمستهلك (طلب الوصفات، إشعار المرضى، توجيه الصيدليات). يتشاركان متطلبات تنظيمية - التعامل مع بيانات المرضى، صحة الوصفات، مسارات التدقيق - لكن كل شيء آخر مختلف تقريباً.

الجانب الموجّه للمستهلك له دورات تكرار أسرع، علاقات شراكة تكامل أقل رسمية، وضغط أكبر للتعامل مع الحالات الحدية الطويلة في البيانات (التنسيق الغريب للوصفة، الصيدلية التي لا تستجيب أيام العطلة). الجانب المؤسسي لديه تكامل أكثر هيكلة، عقود HL7 وFHIR رسمية، ودورات تغيير أبطأ. كلاهما صعب. المدى مفيد.

أبني Fulcrum - منصة تحكم وكلاء محلية أولاً - جزئياً لأن أنماط التنسيق التي تعلمتها في الـbackends الموزّعة للرعاية الصحية تنطبق مباشرة على تنسيق الوكلاء. التسليم الموثوق، ومعالجة الرسائل الميتة، وعقود الأحداث ذات الإصدار: المشاكل هي نفسها، الدومين مختلف.