meinrezept.online 処方箋プラットフォーム
ハンブルクのオンライン処方箋プラットフォーム。バックエンドリードとして PHP/Node.js マイクロサービスと RabbitMQ による薬局ルーティングをパンデミック負荷下で構築した記録。
2020年4月、ドイツのすべてのヘルスケアスタートアップが突然重要になった。
薬局は圧倒されていた。医師は初めてビデオ診察をしていた。患者は紙の処方箋を郵送していた。国全体が目を覚まして、処方箋パイプライン—医師が処方箋を書いてから患者が薬を受け取るまでの道のり—がほぼ完全にアナログであることに気づいた。ハンブルクで、meinrezept.online にバックエンドリードの一人として参加し、その真ん中に座るシステムの構築を手伝った。
これは「パンデミック中に命を救った」という投稿じゃない。「エンジニアリングが実際にどう見えたか」という投稿だ—なぜならエンジニアリングは本当に興味深くて、そのほとんどが外から見えないからだ。
処方箋フォーマットの問題
処方箋を薬局にルーティングする前に、それを読む必要がある。2020年のドイツでは、これは複数のフォーマットを同時に扱うことを意味した:Muster-16 紙のフォーム、まだ標準化されていない初期のデジタル処方箋フォーマット、ファックス画像、そして医師のオフィスが使っている診療所管理ソフトウェアから生成された PDF。
各フォーマットは異なる抽出パスを持つ。紙のフォームは既知のレイアウトを持つがスキャンとして届く。デジタルフォーマットは構造化データを持つが、すべての薬局がそれらすべてを消費できるわけじゃなかった。診療所管理ソフトウェアからの PDF は最悪のケースだ—プログラム的にパースされることを意図していないドキュメントフォーマットの中にロックされた構造化データで、ドイツ政府が電子処方箋(eRezept)のロールアウトをリアルタイムで推進する中でアクティブに進化しているフォーマットで。
バックエンドの仕事は、これらすべてをシステムの残り—薬局ルーティングレイヤー、注文追跡サービス、患者通知サービス—が入力フォーマットを気にせずに消費できる単一の内部表現に正規化することだった。これは入力フォーマットが動いている間は聞こえるよりも難しい問題だ。
薬局インテグレーションは設計上レガシー重い
バックエンドから薬局インテグレーション空間がどう見えるかを示す:すべての主要な薬局チェーンは独自の注文システムを持っている;多くの独立系薬局は WINAPO、ProFit、ADG を動かしている—数十年前の診療所管理システムでモダンなバックエンドコンテキストでは聞いたことのないプロトコル経由で注文インターフェースを公開する。HTTP/JSON は支配的なワイヤフォーマットじゃない。EDIFACT と HL7 のバリアントが出てくる。一部の薬局にはウェブポータルと誰かがリバースエンジニアリングした非公式 API がある。一部には何もなくて、構造化されたメールを送ることに戻る。
これは不満じゃない。消費者向けエンドでのヘルスケアインテグレーションの見た目だ。以前やっていた病院規模の EHR 作業(AFAQ、HAKEEM)は異なる抽象レベルで動く—専用のインテグレーションエンジン、HL7 FHIR コントラクト、相手のシステムの技術的な対応者。消費者向け薬局スケールでは、薬局が実際に持っているものとー緒に動く、それは頻繁に自分が選ぶよりもずっと少ない。
RabbitMQ はメッセージブローカーで、これはこのスケールに正確に正しかった。Kafka レベルのボリュームじゃない—リトライセマンティクス、デッドレター処理、インバウンド注文受け取りとアウトバウンド薬局ディスパッチのデカップリングを持つ管理可能な数のサービス間の信頼性の高い配信。RabbitMQ はこれをクリーンに処理する。エクスチェンジトポロジーは薬局地域、処方箋タイプ、フルフィルメントパスでルーティングするトピックエクスチェンジを使った—これにより、すべてのコンシューマーに触れることなくルーティングロジックを追加できた。
三言語のマイクロサービス
スタックは PHP 7、Python、Node.js だった。これが現実の世界だ。最初のサービスが書かれた言語を引き継ぐ。次の問題に最適なツールを選ぶ—抽出作業に Python、リアルタイムステータス API に Node.js。言語の一貫性ではなくメッセージコントラクトで結びついたポリグロットのコードベースで終わる。
正直なところ、メッセージコントラクトの規律は言語の一貫性より重要だ。すべてのサービスが order.received イベントがどう見えるかに同意していれば—スキーマ、バージョニング、必須フィールド—プロデューサーが PHP でコンシューマーが Node.js という事実は実装の詳細だ。危険な障害モードは、各サービスがドメインの理解を独立して進化させ、order.received が薬局ディスパッチサービスと患者通知サービスでは少し違う意味になることだ。最悪のタイミングで気づく:患者が「処方箋が発送された」通知を受け取ったのに薬局に注文の記録がないと言ってサポートに電話してくるとき。
フロントエンドの ReactJS—主に自分が担当したわけじゃない—は状態 API を通じて同じドメインモデルに接続した。患者が UI で見るものとバックエンドのイベントログが言うことの一貫性は、ステージング環境が予測していなかったタイミングのギャップをパンデミック時代の注文ボリュームが明らかにしたときに見えるようになった過小評価されたインテグレーションの懸念だ。
パンデミックのタイムラインがすべてを増幅した
2020年4月に参加することは、製品がステージング環境がモデル化していない方法で本物のユーザー需要によって負荷テストされているチームに参加することを意味した。ヘルスケアの不安は本物のトラフィックドライバーだ。インバウンド注文ボリュームはあらゆるニュースイベント—ロックダウン発表、薬局営業時間の変更、薬の不足についてのニュース記事—周辺でスパイクする。サービスのどの部分がステートフルでどれがそうじゃないかを非常に速く学ぶ、なぜならステートフルな部分が負荷スパイク下で非優雅に失敗するものだから。
抽出パイプラインを注文受け入れパスからデカップリングすることが重要なアーキテクチャ上の呼びかけだった。遅い薬局インテグレーションが患者の処方箋が受信・確認されることを防ぐべきじゃない。確認とフルフィルメントは異なる懸念であり、独立して失敗すべきだ。この変更は負荷下で行われた、これは理想的じゃない—でも本物のエンジニアリングの記憶だ、そして推論は振り返りとホワイトボードセッションの両方で正しい。
ヘルスケアスケールをまたいで得られるもの
二つの非常に異なるスケールでヘルスケアソフトウェアをリリースしてきた:大規模エンタープライズ EHR と臨床ワークフローシステム(病院規模、何十万もの患者記録、複雑な臨床決定支援)、そして消費者向けヘルスケアアプリ(処方箋注文、患者通知、薬局ルーティング)。両方が規制要件を共有する—患者データ処理、処方箋の有効性、監査証跡—しかしほとんど他のものは共有しない。
消費者向けサイドはより速いイテレーションサイクル、より形式的でないインテグレーションパートナーの関係、データのロングテールのエッジケースを処理するより多くのプレッシャーを持つ。エンタープライズサイドは構造化されたインテグレーション、正式な HL7 と FHIR コントラクト、より遅い変更サイクルを持つ。どちらも難しい。その範囲は役立つ。
meinrezept.online はまだ動いている。この時期の薬局ルーティングと処方箋処理インフラ—本当に異常なプレッシャー下で、進化する規制の景観に対して、同じロックダウンを経験しているチームがそれが支えているシステムを構築した—はその運営の一部だ。パイプはまだ動いている。