BT

最新技術を追い求めるデベロッパのための情報コミュニティ

寄稿

Topics

地域を選ぶ

InfoQ ホームページ アーティクル ケーススタディ:ブラジル国民医療システム

ケーススタディ:ブラジル国民医療システム

ブラジルの国民医療システムは、2百万行を越すコードと350クラスのドメインモデルを有しており、今までに構築された最も大きなエンタープライズJavaアプリケーションだと言われています。アプリケーションは、全国規模の医療システムとして考えられるすべてのドメインコンセプトをモデル化しています。そして、ブラジルの人々のためばかりでなく、公共の医療システムのためにも、非常に大きな価値を創出するほどの水準の自動化をもたらしています。この種のケースでは唯一である本ケーススタディでは、アーキテクチャ、興味深いソリューション、学んだ教訓、プロジェクトの将来の方向性について、その詳細に目を向けます。

問題領域

ブラジルは完全に無料の公共医療システムを持つ世界中で数少ない国のうちの1つです。ただし、このような規模のあらゆる公益事業と同様に、多くの運用上の問題がありました。システムの大部分は紙ベースであり、既存のITシステムおよび国や地域の医療機関の間で、システムはほとんど統合されていませんでした。その例を示します。

  1. 医療機関がお互いに患者の予約を入れることのできるスケジュール管理システムがありませんでした。例えば、心臓の専門医を訪れなければならない患者は、空いている専門医を自分自身で探す必要がありましたが、そのためには、十分に早い空きを見つけるまで何人もの専門医の長い列に並び、複数の予約を取るという方法がよく使われました。
  2. 手術歴、治療中のあらゆる時点における病状などの、患者の病歴についての情報がありませんでした。
  3. 同じ患者が、異なるいくつものデータベースに何回も登録されるということがよくありました。
  4. 出生、死亡、医療手術、病気の診断などの重要な統計データが、統合されていない紙ベースのシステムに保存されていたため、政府は医療資源の容量の計画や不足への対処ができませんでした。
  5. 医療法規や方針をモデル化したITシステムが治療管理システムに統合されていなかったため、医療システムの不正使用の検出や防止を行えませんでした。
  6. 地方の医者の多くが情報システムを全く持っておらず、スケジュールが管理されていなかったため、患者は一日中待たなければなりませんでした。

これらの問題を念頭に入れて、Siga Saudeと呼ばれる医療自動化システムの構築が発注されました。このシステムは、スケジュール管理、医者や機器の在庫管理、請求書作成、疾病追跡、報告書作成/会計監査、規制順守および機密アクセス管理などの、公共の医療情報システムとして考えられるすべての面を処理するように設計されています。

システムは最初にサンパウロ市のすべての医療ユニットを自動化するために配備されました。サンパウロ市はブラジルで最大の都市で、世界では第4位、2千万人を超える人口を有しています。今日では、アプリケーションはサンパウロと他の20の都市で稼動しています。そして、ブラジルの他の都市だけではなく、ポルトガル語を公用語とするアンゴラやモザンビークなど、自国の医療システムの自動化にも興味を持つ他の国にまでシステムは拡張しつつあります。

ソリューション概要

アプリケーションはEJB 2.1 + Strutsをベースにして、データ転送オブジェクト、セッションファサード、サービスロケータ、そしてビジネスデリゲートなど、確立されたEJBデザインパターンを使用して、明確に定義された階層アーキテクチャを活用しています。開発はEclipseで行われ、テストとデプロイはJBossアプリケーションサーバで実行されました。ある分野ではルールエンジン(Drools)が使われ、そして現在、アプリケーションは、非クラスターDual Xeon 3.1サーバにデプロイされています。サーバは4GBのRAMを持つLinuxでJBoss 3.2.7が動作しています。

構想から要件や開発まで、医者や医療の専門家を含む150人を超える人々が携わり、システムはどうあるべきかを明確にしました。開発チームはシステムを次のようなモジュールに分類しました。

上記のモジュールのコード数は、サービス層と、このようなモジュール特有のドメインモデルに含まれる行数です。さらに10万行のコードがサービス層のモジュールで共有され、また57万行のコードがドメインモデルで共有されています。本当のコード数は気が遠くなるような数かもしれませんが、後ほど記述するアノテーションの使用についてのドリルダウンにあるように、実際はコード数のうち58%は生成されたコードです。

上記のビジネスモデルはそれぞれJSPページ、Strutsアクション、そしてビジネスデリゲートから成っており、サービス層を呼び出します。サービス層は生成されたセッションファサードを持つEJB 2.1のセッションビーンから成ります。ドメインモデルは、エンティティビーンを作成するのに使われるアノテーション付きのPOJOで構成されます。次のドリルダウンでは、スケジュール管理システムを、urlからsqlまで詳細に渡って説明します。このモジュールの全体的な階層構造は他と同じであり、システムがどのように構築されているか深く理解できるにちがいありません。

ドリルダウン:専門医の予約のスケジュール管理を行うユースケース

このシステムにおいて、ビジネスと技術の両方の見地から最も重要なユースケースの1つは、地方の小さな診療所の受付係が患者のために専門医の診察の予約を取れる、というケースです。このユースケースはスケジュール管理モジュールで実装されましたが、システムの他の多くのモジュールにも影響を与えています。スケジュールの予約というユースケースは次のようになっています。

まず、受付係はスケジュールの中に空いている時間枠を探そうとします。受付係は、特定の医者、専門性、医療機器の種類、特定の医療処置などによる問い合わせを発信することができます(ここ(JPG・英語)をクリックすると入力画面を参照できます)。下のシーケンス図にある“getSlots”を見てください。空いた時間枠が見つかると(シーケンス図のchooseSlot())、National Health Cardモジュールを使って、患者の健康手帳のバーコードを走査するか、手帳を忘れた場合は、名前、生年月日、または他の連絡先情報を調べることによって(searchPatient()を参照)、患者のプロフィールを探します。それから同じ画面上で必要な処置の種類を入力します(2番目の入力画面参照(JPG・英語)、シーケンス図のsaveAppointment()参照)。空きがないとか、診療所がそのような処置を行っていない、という理由で、その診療所では患者が必要な処置が受けられない場合は、受付係または医者が他の診療所の空き時間を探せます。

このシーケンス図は、典型的なユースケースがシステムでどのように実行されるのかを示しています。まず、Strutsのアクションクラス(ScheduleAction)がユーザーの要求を処理し、プレゼンテーション・ロジックに対応します。データの取り込みやビジネスルールの呼び出しが必要な場合、アクションクラスは、ビジネスデリゲート・デザインパターン(ScheduleCF)を実装したPOJOオブジェクトのメソッドを呼び出します。このクラスは自動的にセッションファサード・クラス(ScheduleSF)から作成され、サービス層に使われている技術にウェブ層が依存しないようにしています。セッションファサード・デザインパターン(ScheduleSF)を実装したセッションビーンは、ビジネスルールの実行を他のクラスに委ねます。例えば、AppointmentServiceクラスは、lockSlot()メソッドとsearchForSlot()メソッドを実行します。ユースケースがデータを保持する必要があるときは、setAppointmentVO()メソッドがエンティティビーン上で呼び出されます。検索は、エンティティビーンの仕組みを使うのではなく、Searcherと呼ばれる最適化された検索コンポーネントを使って行います。

セッションファサード・オブジェクトは、トランザクション・サービス、データベース接続、および他のアプリケーションへのセキュリティ・サービスの提供を担当しています。サービス・オブジェクトはPOJOであり、エンティティビーンに直接Web層からアクセスすることができないので、セッションビーン層が必要とされました。

時間枠がロックされると、タイムスタンプとロックIDが時間枠データベース・テーブルに保存されます。競合状態を防ぐため、これらのデータは両方ともSELECT FOR UPDATEコマンドで保存されます。タイムスタンプは、ロックが時間切れになったかどうかを判別するために使われます。ロックIDは、オブジェクトが時間切れになったロックを保存しようとする状況を避けるために使われます。ロックを要求したオブジェクトが予約を保存することに決めると、受け取ったロックIDとテーブルに保存されたロックIDを比較します。それらが異なる場合、新しい情報は保存できません。このように、ロックの要求と予約の保存の間にロックが時間切れになった場合は、アプリケーションはこれを検出してユーザに通知します。このユースケースは論理ロックを実装しています。データベースロックやEJBロックは長い時間保持されないので、ビジネスルールの方がうまく実装できます。.

このユースケースに関与したドメインオブジェクトの(簡素化した)クラス図を以下に示します。

ドリルダウン:コードの生成にアノテーションを使用する

プロジェクトが開始されたとき、配置記述ファイル、値オブジェクト、クエリ、セッションファサード、Strutsフォームおよびアクション、データの妥当性確認、そしてローカルおよびリモートEJBインターフェースを生成するのに、XDoclet 1が使われました。コード生成はこのプロジェクトの主要な戦略でした。プロジェクトの開発期間が短く、開発者の生産性を上げ、バグの数を減らす技術が必要だったためです。そこで、XDocletの元々のテンプレートは”専門家のコード“を生成するように変更されました。

XDocletのアプローチは、プロジェクト開始後10ヶ月はうまくいき、開発者が必要とする生産性を上げる助けとなりました。それからしばらくして、コードが増加するにつれて生成時間もまた増加し、それによって生産性が深刻な影響を受けるに至りました。そこでチームは、Java 5 アノテーションを使用してコード生成を行う戦略に変更することに決めました。

アノテーションを使用する戦略によって、いくつかのXDocletの難点を解決することができました。

  • 配置記述ファイルのようなファイルの増加分の生成が可能になりました。 XDocletは、1つのクラスが変更されるとすべてのクラスを再び読み込んで、新しい配置記述ファイルを生成します。アノテーションを使用することで、クラスの変更によって影響を受けた配置記述ファイルの一部分だけを生成することができます。開発者がほんの少しのクラスしか変更しない場合がほとんどなので、これによって多くの時間の節約になります。
  • XDoclet 1は、生成時にサブコンポーネントのソースコードにアクセスする必要があり、それが結合や構築の複雑さを増加させていました。コンパイルされたjarだけにアクセスすればよい、というのが望ましいのですが、すべての開発者はすべてのソースコードにアクセスしなければなりませんでした。アノテーションを使うことで、この問題を回避できました。
  • 実行時にアノテーションを処理することができるので、コードを生成するかわりに、この機能を使うことでうまくいくこともあります。

チームは、EJB 3.0アノテーション用の処理装置と、他にXDocletが生成するのとよく似たコードを生成するような、カスタムメイドのアノテーション用処理装置を開発しました。EJB 3アノテーションが使われていましたが、生成されるコードはEJB 2.1に対応していました。

アノテーション用処理装置のテンプレートとしてVelocityテンプレートが使われました。XDocletから独自のテンプレートに移行する際に最も骨の折れる課題は、XDoclet 1と同じくらい幅広く、テンプレートとヘルパークラスのセットを作り上げることでした。

アノテーションを使うことによって、生成時間はかなり減少しました。1つのクラスが変更された場合、XDocketを使うと400クラスからコードを生成するのに1分50秒かかっていました。APTだと同じ処理が10秒で終わりました。

ドリルダウン:ルールエンジンがビジネスロジックを単純化した方法

システムの一部分ではビジネスルールを扱う必要がありましたが、それは都市や政府法案によって異なり、また時間の経過とともに変わります。例えば、診療所がX線を使った処置を行おうとしたら、X線装置を有することや、公認されたX線技師がいることなどのいくつかの規則に従わなければなりません。このような規則はしばしば変更されるので、これらはコードの外に置く方が無難です。そうすれば、コードを変更することなく規則を変更できます。

Siga Saudeでは、javaのルールエンジンAPIでJSR-94の実装であるDrools(サイト・英語)ルールエンジンを使ってこの問題を解決しました。ルールの処理は、Decisionと呼ばれるSiga Saudeのコンポーネントによって行われます。このコンポーネントはルールセットと作業メモリを使います。ルールセットは一連のメッセージを使いますが、その際に各ルールは1つのメッセージを持ち、それはbr.com.vidatis.common.decision.message.RuleMessageインターフェースを実装するクラスによって記述されます。1つのルールもしくはルールのサブセットが満たされると、関連したメッセージは待ち行列から削除されます。このように、ルール処理を追跡することができます。そして最後に待ち行列にメッセージがなくなると、ルールはすべて満たされます。ルールは、新しいルールセットを含む、いくつかの異なるアクションを起動することができます。これらのルールはXMLファイル内に記述され、Droolsエンジンによって処理されます。

XMLファイルの例

<rule name="if_clinic_code_then_checksum_digit_valid">
<parameter identifier="clinic">
<java:class>br.atech.smssp.domain.clinic.vo.ClinicVO</java:class>

</parameter>
<java:condition>clinic.getCode() != null</java:condition>
<java:condition>!clinic.getCode().equals("")</java:condition>
<java:consequence>
//clinic code should be mandatory and valid
if(br.com.vidatis.common.decision.rule.CodeRule.isValidCone(clinic.getCode())){
ruleMessage.markRuleAsValid("if_clinic_code_then_checksum_digit_valid");
}
</java:consequence>

</rule>

<rule name="if_maintainer_code_then_checksum_digit_valid">
<parameter identifier="clinic">
<java:class>br.atech.smssp.domain.clinic.vo.ClinicVO</java:class>
</parameter>
<java:condition>clinic.getMaintainerCode() != null</java:condition>

<java:condition>!clinic.getMaintainerCode().equals("")</java:condition>
<java:consequence>
//Maintainer code is optional, but should be valid if it is informed.
if(br.com.vidatis.common.decision.rule.CodeRule.isValidCode(clinic.getMaintainerCode())){
ruleMessage.markRuleAsValid("if_maintainer_code_then_checksum_digit_valid");
}
</java:consequence>
</rule>

<rule name="if_maintainer_code_then_checksum_digit_valid_OPTIONAL_EMPTY">
<parameter identifier="clinic">

<java:class>br.atech.smssp.domain.clinic.vo.ClinicVO</java:class>
</parameter>
<java:condition>clinic.getMaintainerCode() != null</java:condition>
<java:condition>clinic.getMaintainerCode().equals("")</java:condition>
<java:consequence>

//Maintainer code is optional and can be empty
ruleMessage.markRuleAsValid("if_maintainer_code_then_checksum_digit_valid");
</java:consequence>
</rule>

<rule name="if_maintainer_code_then_checksum_digit_valid_OPTIONAL_NULL">
<parameter identifier="clinic">
<java:class>br.atech.smssp.domain.clinic.vo.ClinicVO</java:class>

</parameter>
<java:condition>clinic.getMaintainerCode() == null</java:condition>
<java:consequence>
//Maintainer code is optional and can be NULL
ruleMessage.markRuleAsValid("if_maintainer_code_then_checksum_digit_valid");
</java:consequence>
</rule>

これは、診療所に関するデータを保存しようとする例です。サービスクラスがDroolsルールエンジンを呼び出して、認証規則の適用を要求しようとしています。すべての規則に合格するとデータは無事に保存されます。このような認証規則は政府による法規制の変更に伴って変わります。規則は頻繁に変更されるので、コードから認証規則から分離することは重要です。そのようにすることで、アプリケーションを再起動せずに保守作業を行えます。

学んだ教訓

コード生成はこのプロジェクトの主要な成功要因でした。それによって開発者の生産性が向上し、コードが均一になりました。50人もの開発者が全員同じ基本コンポーネントを共有しながら、それぞれ独立したチームで働いているような場合に、均一なコードを持つということは非常に重要です。XDocletを使いましたが、しばらくすると、生成時間が非常に長くなるという好ましからざる副作用を生じました。アノテーションに変えることで、生成時間を減らしながらもコード生成の恩恵に与ることができました。

意思の疎通が良好であることは不可欠です。今回のような巨大なプロジェクトには、時おり違う言葉を話しているのではないかと思えるような様々な“種族”がいます。彼らにお互いの考え方を理解してもらうのは難しく、またイライラすることもある作業です。特にこのような種族間の意思疎通はなかなか厄介です。 

  • 開発者 対 アーキテクト - 私たちは自分のたちの出来る仕事をすべきです。アーキテクトが積極的に開発活動に参加すると、より良いソリューションを提案し、生産性を上げることができます。これがチームの“幸福度”を上げるのに役立ち、それがまた生産性に影響を与えます。
  • 要件分析者/顧客 対 開発者/アーキテクト - 両者の意思疎通がうまくいかないことが、遅れや不満の原因になることがよくあります。よくできた仕様は必須ですが、よいユースケースの仕様だけでは、双方のグループの動機を理解し、チームとして行動させることはできません。
  • スーパー開発者 対 他のスーパー開発者 - プロジェクトには、各メンバがソフトウェアを作る素晴らしいアイディアが詰まった頭脳を持つ、並外れた開発者チームがありました。しかし、開発期間が短かったため、日々彼らの頭に浮かぶ革新的なアイディアをすべて試すのは不可能でした。このことが、チームに欲求不満をもたらしました。このようなプロジェクトでは、明確な目標を保ち続け、なぜこのような決定が成されたのかを全員に理解してもらうことが重要です。この点で助けになったのは、チームが定期的に数週間のリファクタリングを行ったことです。その数週間の間に、チームは問題について、またそれを解決するために何が変えられるかについて議論し、その間にシステムのリファクタリングを行いました。新しいコードを開発することなく、コードを修正するためだけのリファクタリング専用の1週間は、新しいアイディアを利用し、生産性を向上する助けとなります。

ルール、コード生成、デザインパターン、コンポーネントが存在する、明確に定義されたアーキテクチャなら、小規模または中規模のソフトウェアのリファクタリングを行うことは簡単です。大規模のリファクタリングはいつも大変です。また、厳しい規則もアーキテクチャがあまりよく適用されていないアプリケーションの20%の実装を困難にします。アーキテクチャの標準化の実施に関して、どこまでやるべきか理解することは重要です。プロジェクトの最中に、開発者がアーキテクチャにソリューションを適用しましたが、それが最も効果的な方法ではない場合もありました。その一方で、ユーザーインターフェースのように、システムの中で規則があまり厳しくない部分では、開発者が各自でソリューションを選択したために、システムの一部の品質が低下し、コードの保守が複雑になるなど、望ましくない結果を生じました。

巨大なアプリケーションにおいて、依存関係やコードが多い場合は開発サイクルが長くなりがちです。時には、簡単なフィールドを1つ画面に追加することがシステムの多くの部分を変更することになり、時間がかかってしまいます。これは開発者と顧客の双方にとってイライラさせられることです。ツールがとても重くなり、すべてが本来の時間より長くかかるように思えます。アプリケーションをいくつかのコンポーネントに分割しましたが、問題を完全に解決するには十分ではありませんでした。J2EEにはメリットがありますが、コードがさらに複雑になるという点もあり、これは生産性の面では高くつき、経営者や顧客に説明するのに骨の折れるところです。

チームがこのプロジェクトを開始した頃、こんな開発期間で行うのは無理だと多くの人々に言われました。他の国々では、公共の医療情報システムを構築する際に苦い出来事が数多く経験されてきました。ですから、この経験から学ぶ最後の教訓はこうです。「人が無理だと言ったとき、その通りにしたら成し遂げることはできません。」

将来の方向性

コンテナの外からのテストの実現

コンテナの外からテストができないことはチームを本当に煩わせています。当初それができなかったのは、旧式のコードから始めざるを得なかったからです。政府の規則および法規制のモジュールは膨大なEJB 1.0のシステムでした。すべてのエンティティビーンおよびコンテナの他の加工品はコード生成を使用して作成され、アプリケーションの新しい部分は次第にPOJOベースになってきています。しかしながら、コンテナにデプロイすることは、変更した部分だけですら、開発者たちが喜んで受け入れることが出来ないほど時間がかかっています。

POJOベースのアーキテクチャへの移行

現在のシステムには明らかに多くのコンテナ依存とEJB配管コードが存在します(生成されたビジネスデリゲートおよびセッションファサードによってほとんど隠されていますが)。しかし、移行しようとしているのは、ビジネスルールを含む、完全にPOJOベースのアーキテクチャです。チームは、すべてのセッションビーンを1つの単一のビーン・インターセプターにリファクタリングしたいと考えています。そうすることで呼び出しはサービス層に委ねられます。セッションビーン・インターセプタの目的は、トランザクションおよびスレッド管理などのミドルウェアサービスの提供を簡単にすることです。Springを利用しようと考えていましたが、既存のアーキテクチャを変更することをかんがみると、費用がかかると考えられました。既存のセッションビーンはすべて生成されたものなので、EJBから完全に消してしまうのではなく、セッションビーン・インターセプターを通るようにコード生成ロジックを変更すればよく、非常に簡単です。このようにリファクタリングを可能にしてくれるもう1つの要素として、このような膨大な変更をウェブ層から隠すビジネスデリゲート層の存在があります。

なぜEJBを完全に終わりにしてしまわないのでしょう? その理由は、システムにアクセスする必要がある保健省の他のグループに、ゆくゆくはアプリケーションを公開できるようにする必要があるからです。
チームはすぐに使えるビジネスデリゲートを、(サンパウロから来る)人々に手渡すだけですむように考えています。ビジネスデリゲートは遠隔処理のすべての面を隠すので、ネットワーク・パートナーは次第にJavaから手を引いていくだろうとわかっていますが、統合は全く面倒なく行われると考えられます。

異なるトランザクションタイプの区別を、どのようにして明らかにするのでしょうか?異なるトランザクションの区分別に異なるメソッドを持たせ、ターゲットメソッドにトランザクションタイプをマッピングしようとしていますので、どれが実行すべきメソッドを呼び出すのかがわかります。偶然にも、このようなセッションビーン・インターセプタとトランザクション戦略について非常によく似た記述がFloyd Marinescu氏のEJBデザインパターンという本に載っています。:)

結局は、おそらく直接Hibernateを使用して値オブジェクトを保持するだけにして、ゆくゆくはエンティティビーンを終わりにしたいと思っています。将来このように変更する際に直面する事のうち、最も高くつくのはテストです。徹底したテストを行わなければなりませんし、単なる単体テストではすべてを網羅できないでしょう。

ウェブのUIを簡単にするAJAX

AJAXは、技術者ではないエンドユーザーのためのUIワークフローを簡単にするためのツールとして見なされました。例えば、スケジュール管理ユースケースで、医者の名前、治療の名前、専門分野の名前などを入力する際に、オートコンプリートはその助けとなることができました。ポップアップの呼び出しや検索を行うことなく、大きなリストの中から選択すればいいのです。また、長くて大きいフォームを使って作業するときは、間違えてクリックしてページを消したり、作業内容を失ってしまったりすることのないように、1つずつHTTPSessionに送られる画面のフィールドの値を保持してくれるAJAXを使う方が賢明です。

AJAXは最近すでにデプロイされました。AJAXは階層化された膨大な手続きをナビゲートし、その一方で、必要であれば要素を検索するのに使われています。これによってより良いインターフェースをユーザに提供できるようになり、データの入力時間が減少しました。ここ(JPG・英語)をクリックすると画面例を参照できます。

原文はこちらです:http://www.infoq.com/articles/Brasilian-Healthcare-System

(このArticleは2006年5月12日にリリースされました)

この記事に星をつける

おすすめ度
スタイル

BT