GAE開発の落とし穴
Googleのクラウド環境をつかったGoogle App Engineによる開発するにあたり、初めての試みで苦悩する開発者達の経験をもとに、各開発フェーズにあわせて問題点やどう解決したかをご紹介します
ブックマークされました!
ブックマークがエラーになりました。もう一度お願いします。

作者 Fabiane Nardon, Floyd Marinescu , 翻訳者 編集部 投稿日 2007年9月18日
ブラジルの国民医療システムは、2百万行を越すコードと350クラスのドメインモデルを有しており、今までに構築された最も大きなエンタープライズJavaアプリケーションだと言われています。アプリケーションは、全国規模の医療システムとして考えられるすべてのドメインコンセプトをモデル化しています。そして、ブラジルの人々のためばかりでなく、公共の医療システムのためにも、非常に大きな価値を創出するほどの水準の自動化をもたらしています。この種のケースでは唯一である本ケーススタディでは、アーキテクチャ、興味深いソリューション、学んだ教訓、プロジェクトの将来の方向性について、その詳細に目を向けます。
ブラジルは完全に無料の公共医療システムを持つ世界中で数少ない国のうちの1つです。ただし、このような規模のあらゆる公益事業と同様に、多くの運用上の問題がありました。システムの大部分は紙ベースであり、既存のITシステムおよび国や地域の医療機関の間で、システムはほとんど統合されていませんでした。その例を示します。
これらの問題を念頭に入れて、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の難点を解決することができました。
チームは、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を使いましたが、しばらくすると、生成時間が非常に長くなるという好ましからざる副作用を生じました。アノテーションに変えることで、生成時間を減らしながらもコード生成の恩恵に与ることができました。
意思の疎通が良好であることは不可欠です。今回のような巨大なプロジェクトには、時おり違う言葉を話しているのではないかと思えるような様々な“種族”がいます。彼らにお互いの考え方を理解してもらうのは難しく、またイライラすることもある作業です。特にこのような種族間の意思疎通はなかなか厄介です。
ルール、コード生成、デザインパターン、コンポーネントが存在する、明確に定義されたアーキテクチャなら、小規模または中規模のソフトウェアのリファクタリングを行うことは簡単です。大規模のリファクタリングはいつも大変です。また、厳しい規則もアーキテクチャがあまりよく適用されていないアプリケーションの20%の実装を困難にします。アーキテクチャの標準化の実施に関して、どこまでやるべきか理解することは重要です。プロジェクトの最中に、開発者がアーキテクチャにソリューションを適用しましたが、それが最も効果的な方法ではない場合もありました。その一方で、ユーザーインターフェースのように、システムの中で規則があまり厳しくない部分では、開発者が各自でソリューションを選択したために、システムの一部の品質が低下し、コードの保守が複雑になるなど、望ましくない結果を生じました。
巨大なアプリケーションにおいて、依存関係やコードが多い場合は開発サイクルが長くなりがちです。時には、簡単なフィールドを1つ画面に追加することがシステムの多くの部分を変更することになり、時間がかかってしまいます。これは開発者と顧客の双方にとってイライラさせられることです。ツールがとても重くなり、すべてが本来の時間より長くかかるように思えます。アプリケーションをいくつかのコンポーネントに分割しましたが、問題を完全に解決するには十分ではありませんでした。J2EEにはメリットがありますが、コードがさらに複雑になるという点もあり、これは生産性の面では高くつき、経営者や顧客に説明するのに骨の折れるところです。
チームがこのプロジェクトを開始した頃、こんな開発期間で行うのは無理だと多くの人々に言われました。他の国々では、公共の医療情報システムを構築する際に苦い出来事が数多く経験されてきました。ですから、この経験から学ぶ最後の教訓はこうです。「人が無理だと言ったとき、その通りにしたら成し遂げることはできません。」
コンテナの外からテストができないことはチームを本当に煩わせています。当初それができなかったのは、旧式のコードから始めざるを得なかったからです。政府の規則および法規制のモジュールは膨大なEJB 1.0のシステムでした。すべてのエンティティビーンおよびコンテナの他の加工品はコード生成を使用して作成され、アプリケーションの新しい部分は次第にPOJOベースになってきています。しかしながら、コンテナにデプロイすることは、変更した部分だけですら、開発者たちが喜んで受け入れることが出来ないほど時間がかかっています。
現在のシステムには明らかに多くのコンテナ依存とEJB配管コードが存在します(生成されたビジネスデリゲートおよびセッションファサードによってほとんど隠されていますが)。しかし、移行しようとしているのは、ビジネスルールを含む、完全にPOJOベースのアーキテクチャです。チームは、すべてのセッションビーンを1つの単一のビーン・インターセプターにリファクタリングしたいと考えています。そうすることで呼び出しはサービス層に委ねられます。セッションビーン・インターセプタの目的は、トランザクションおよびスレッド管理などのミドルウェアサービスの提供を簡単にすることです。Springを利用しようと考えていましたが、既存のアーキテクチャを変更することをかんがみると、費用がかかると考えられました。既存のセッションビーンはすべて生成されたものなので、EJBから完全に消してしまうのではなく、セッションビーン・インターセプターを通るようにコード生成ロジックを変更すればよく、非常に簡単です。このようにリファクタリングを可能にしてくれるもう1つの要素として、このような膨大な変更をウェブ層から隠すビジネスデリゲート層の存在があります。
なぜEJBを完全に終わりにしてしまわないのでしょう? その理由は、システムにアクセスする必要がある保健省の他のグループに、ゆくゆくはアプリケーションを公開できるようにする必要があるからです。
チームはすぐに使えるビジネスデリゲートを、(サンパウロから来る)人々に手渡すだけですむように考えています。ビジネスデリゲートは遠隔処理のすべての面を隠すので、ネットワーク・パートナーは次第にJavaから手を引いていくだろうとわかっていますが、統合は全く面倒なく行われると考えられます。
異なるトランザクションタイプの区別を、どのようにして明らかにするのでしょうか?異なるトランザクションの区分別に異なるメソッドを持たせ、ターゲットメソッドにトランザクションタイプをマッピングしようとしていますので、どれが実行すべきメソッドを呼び出すのかがわかります。偶然にも、このようなセッションビーン・インターセプタとトランザクション戦略について非常によく似た記述がFloyd Marinescu氏のEJBデザインパターンという本に載っています。:)
結局は、おそらく直接Hibernateを使用して値オブジェクトを保持するだけにして、ゆくゆくはエンティティビーンを終わりにしたいと思っています。将来このように変更する際に直面する事のうち、最も高くつくのはテストです。徹底したテストを行わなければなりませんし、単なる単体テストではすべてを網羅できないでしょう。
AJAXは、技術者ではないエンドユーザーのためのUIワークフローを簡単にするためのツールとして見なされました。例えば、スケジュール管理ユースケースで、医者の名前、治療の名前、専門分野の名前などを入力する際に、オートコンプリートはその助けとなることができました。ポップアップの呼び出しや検索を行うことなく、大きなリストの中から選択すればいいのです。また、長くて大きいフォームを使って作業するときは、間違えてクリックしてページを消したり、作業内容を失ってしまったりすることのないように、1つずつHTTPSessionに送られる画面のフィールドの値を保持してくれるAJAXを使う方が賢明です。
AJAXは最近すでにデプロイされました。AJAXは階層化された膨大な手続きをナビゲートし、その一方で、必要であれば要素を検索するのに使われています。これによってより良いインターフェースをユーザに提供できるようになり、データの入力時間が減少しました。ここ(JPG・英語)をクリックすると画面例を参照できます。
原文はこちらです:http://www.infoq.com/articles/Brasilian-Healthcare-System
Googleのクラウド環境をつかったGoogle App Engineによる開発するにあたり、初めての試みで苦悩する開発者達の経験をもとに、各開発フェーズにあわせて問題点やどう解決したかをご紹介します
去る1月12日、定理証明支援系ツールCoqの初心者向けチュートリアルが開催さ れた(http://kokucheese.com/event/index/23667/)。今後も2月2日 (http://kokucheese.com/event/index/23744/)、2月9日、2月16日と引き続き開 催されていく予定である。本記事では、開催の様子をレポートする。
Neal Gafter氏はOracleによるJava買収の影響に関する議論、Javaにセグメンテッドスタックやメタオブジェクトプロトコルを追加することについての主張、そしてJavaとC#との比較について話をしてくれた。
GoogleはVMをともなう新しい言語であり、JSコンパイラでもあるDartをプレビューした。 InfoQはDartのアプリの構築に貢献する文法の裏側を探った:スナップショット、Isolate、モジュール方式
本記事ではCSPベースの「マルチドメイン・モデル検査ツール」である、PAT(Process Analysis Toolkit)について紹介する。モデル検査は、形式手法(Formal Method)という方法論を基礎とする技術であり、複雑さが増大しながらも安全性を求められる、現在のソフトウェア開発の状況に対する処方箋の1つとして注目されている手法である。
前回まで、Jenkinsの幾つかの側面に注目して解説をしてきました。シリーズ最後の今回は、Jenkinsをサービスとして使う方法を紹介します。
Alloyは、MITにて開発された仕様記述言語であり、ツールによる自動解析を使い、インクリメンタルに形式仕様が書けることが特長である。筆者らはAlloy開発者による、Alloyを使った形式手法入門書を翻訳、今夏にオーム社より刊行した。本記事では、Alloyの簡単な概要と、翻訳書『抽象によるソフトウェア設計』(「Alloy本」)を紹介する。
スマートフォンを中心としたマルチデバイスにおけるタッチユーザーインターフェイスへの対応は、既に必須の項目となりつつある。本記事では、Windows デバイスにおける UX のベースとなっている「メトロ」というデザイン言語を掘り下げながら、既存環境を意識しつつもどのようにタッチユーザーインターフェイス開発に取り組んでいくべきであるかについて解説していく。
No comments
スレッド表示 返信