FHIR アダプターを使ってレガシーシステムに FHIR サービスを提供 - リソースの読み取り編
HealthShare、HealthConnect、および InterSystems IRIS ユーザーが使用できる FHIR アダプターツールに関する連載記事を再開しましょう。
前回の記事では、ワークショップをセットアップした小さなアプリケーションを紹介し、FHIR アダプターをインストールした後に IRIS インスタンスにデプロイされたアーキテクチャを説明しました。 この記事では、最も一般的な CRUD(作成、読み取り、更新、削除)操作の 1 つである読み取り操作を実行する方法の例を確認します。ここではリソースの取得によって行います。
リソースとは?
FHIR のリソースはある種の臨床情報です。この情報は、患者(Patient)、臨床検査へのリクエスト(ServiceRequest)、または診断(Condition)などです。 各リソースはそれを構成するデータのタイプのほか、データの制約や他のリソースタイプとの関係を定義します。 リソースごとに、それが格納する情報の拡張が可能であるため、FHIR ポリシーの範囲外のニーズに 80% 対応できます(ユーザーの 80% が使用する要件に対応)。
この記事の例では、最も一般的なリソースである Patient を使用します。 その定義を確認しましょう。
{
"resourceType" : "Patient",
// from Resource: id, meta, implicitRules, and language
// from DomainResource: text, contained, extension, and modifierExtension
"identifier" : [{ Identifier }], // An identifier for this patient
"active" : <boolean>, // Whether this patient's record is in active use
"name" : [{ HumanName }], // A name associated with the patient
"telecom" : [{ ContactPoint }], // A contact detail for the individual
"gender" : "<code>", // male | female | other | unknown
"birthDate" : "<date>", // The date of birth for the individual
// deceased[x]: Indicates if the individual is deceased or not. One of these 2:
"deceasedBoolean" : <boolean>,
"deceasedDateTime" : "<dateTime>",
"address" : [{ Address }], // An address for the individual
"maritalStatus" : { CodeableConcept }, // Marital (civil) status of a patient
// multipleBirth[x]: Whether patient is part of a multiple birth. One of these 2:
"multipleBirthBoolean" : <boolean>,
"multipleBirthInteger" : <integer>,
"photo" : [{ Attachment }], // Image of the patient
"contact" : [{ // A contact party (e.g. guardian, partner, friend) for the patient
"relationship" : [{ CodeableConcept }], // The kind of relationship
"name" : { HumanName }, // I A name associated with the contact person
"telecom" : [{ ContactPoint }], // I A contact detail for the person
"address" : { Address }, // I Address for the contact person
"gender" : "<code>", // male | female | other | unknown
"organization" : { Reference(Organization) }, // I Organization that is associated with the contact
"period" : { Period } // The period during which this contact person or organization is valid to be contacted relating to this patient
}],
"communication" : [{ // A language which may be used to communicate with the patient about his or her health
"language" : { CodeableConcept }, // R! The language which can be used to communicate with the patient about his or her health
"preferred" : <boolean> // Language preference indicator
}],
"generalPractitioner" : [{ Reference(Organization|Practitioner|
PractitionerRole) }], // Patient's nominated primary care provider
"managingOrganization" : { Reference(Organization) }, // Organization that is the custodian of the patient record
"link" : [{ // Link to a Patient or RelatedPerson resource that concerns the same actual individual
"other" : { Reference(Patient|RelatedPerson) }, // R! The other patient or related person resource that the link refers to
"type" : "<code>" // R! replaced-by | replaces | refer | seealso
}]
}ご覧のように、実質的に患者のすべての管理情報のニーズに対応しています。
HIS から患者の取得
前回の記事を覚えていれば、HIS システムのデータベースをシミュレーションする PostgreSQL データベースをデプロイしました。特定の HIS にあるサンプルテーブルを確認してみましょう。
.png)
多くはありませんが、ここでの例には十分です。 patient テーブルをもう少し詳しく見てみましょう。
.png)
ここに、3 つの患者の例があります。ご覧のように、それぞれに一意の識別子(ID)のほか、医療組織に関連性のある一連の管理データがあります。 最初の目標は、1 人の患者の FHIR リソースを取得することです。
患者のクエリ
サーバーから患者データをリクエストするにはどうすればよいでしょうか? FHIR が作成した実装仕様によると、サーバーのアドレス、リソース名、および識別子を使って REST 経由で URL に GET を実行する必要があり、 以下のように呼び出す必要があります。
http://SERVER_PATH/Patient/{id}
この例では、Juan López Hurtado という患者を検索します。この患者の ID は 1 であるため、呼び出す URL は以下のようになります。
http://localhost:52774/Adapter/r4/Patient/1
テストするには、クライアントとして Postman を使用します。 サーバーのレスポンスを確認しましょう。
{
"resourceType": "Patient",
"address": [
{
"city": "TERUEL",
"line": [
"CALLE SUSPIROS 39 2ºA"
],
"postalCode": "98345"
}
],
"birthDate": "1966-11-23",
"gender": "M",
"id": "1",
"identifier": [
{
"type": {
"text": "ID"
},
"value": "1"
},
{
"type": {
"text": "NHC"
},
"value": "588392"
},
{
"type": {
"text": "DNI"
},
"value": "12345678X"
}
],
"name": [
{
"family": "LÓPEZ HURTADO",
"given": [
"JUAN"
]
}
],
"telecom": [
{
"system": "phone",
"value": "844324239"
},
{
"system": "email",
"value": "juanitomaravilla@terra.es"
}
]
}次に、本番環境内でリクエストが通過した経路を詳しく見てみましょう。
.png)
ルートは以下のようになっています。
- BS InteropService にリクエストが到着。
- BS の宛先として構成し、受信した呼び出しの患者識別子がクエリされる BP に転送。
- BO FromAdapterToHIS から HIS データベースにクエリ。
- 患者データを BP に転送し、それを FHIR Patient リソースに変換。
- レスポンスを BS に転送。
BP ProcessFHIRBP で受信するメッセージのタイプを見てみましょう。
.png)
クライアントからどの種類のオペレーションをリクエストされたかを特定するための鍵となる 3 つの属性を見てみましょう。
- Request.RequestMethod: どの種のオペレーションを実行するかを示します。 この例では、患者の検索は GET になります。
- Request.RequestPath: この属性には、サーバーに届いたリクエストのパスが含まれ、操作するリソースを示します。この場合、取得するための具体的な識別子が含まれます。
- Quick.StreamId: FHIR Adapter は Stream に届いたすべての FHIR メッセージを変換し、この属性に保存される識別子が割り当てられます。 この例では、GET を実行しており、FHIR オブジェクトを送信していないため、これは必要ありません。
処理を行う GLP を詳しく分析して、メッセージの流れをさらに見ていきましょう。
ProcessFHIRBP:
本番環境に、ビジネスサービスから受け取る FHIR メッセージングを管理する BPL を実装しました。 どのように実装されているか見てみましょう。
.png)
ステップごとに実行する操作を見てみましょう。
FHIR オブジェクトの管理:
HIS データベースへの接続とデータベースクエリを処理する BO FromAdapterToHIS を呼び出します。
Method ManageFHIR(requestData As HS.FHIRServer.Interop.Request, response As Adapter.Message.FHIRResponse) As%Status
{
set sc = $$$OKset response = ##class(Adapter.Message.FHIRResponse).%New()
if (requestData.Request.RequestPath = "Bundle")
{
If requestData.QuickStreamId '= "" {
Set quickStreamIn = ##class(HS.SDA3.QuickStream).%OpenId(requestData.QuickStreamId,, .tSC)
<span class="hljs-keyword">set</span> dynamicBundle = <span class="hljs-keyword">##class</span>(<span class="hljs-built_in">%DynamicAbstractObject</span>).<span class="hljs-built_in">%FromJSON</span>(quickStreamIn)
<span class="hljs-keyword">set</span> sc = <span class="hljs-built_in">..GetBundle</span>(dynamicBundle, .response)
}
}
elseif (requestData.Request.RequestPath [ "Patient")
{
if (requestData.Request.RequestMethod = "POST")
{
If requestData.QuickStreamId '= "" {
Set quickStreamIn = ##class(HS.SDA3.QuickStream).%OpenId(requestData.QuickStreamId,, .tSC)
<span class="hljs-keyword">set</span> dynamicPatient = <span class="hljs-keyword">##class</span>(<span class="hljs-built_in">%DynamicAbstractObject</span>).<span class="hljs-built_in">%FromJSON</span>(quickStreamIn)
<span class="hljs-keyword">set</span> sc = <span class="hljs-built_in">..InsertPatient</span>(dynamicPatient, .response)
}
}
<span class="hljs-keyword">elseif</span> (requestData.Request.RequestMethod = <span class="hljs-string">"GET"</span>)
{
<span class="hljs-keyword">set</span> patientId = <span class="hljs-built_in">$Piece</span>(requestData.Request.RequestPath,<span class="hljs-string">"/"</span>,<span class="hljs-number">2</span>)
<span class="hljs-keyword">set</span> sc = <span class="hljs-built_in">..GetPatient</span>(patientId, .response)
}
}
Return sc
}
BO は受信した HS.FHIRServer.Interop.Request タイプのメッセージを確認します。この場合は、GET を設定して、Partient リソースに対応するパスに、以下の GetPatient メソッドが呼び出されることを示して確認します。
Method GetPatient(patientId As%String, Output patient As Adapter.Message.FHIRResponse) As%Status
{
Set tSC = $$$OKset sql="SELECT id, name, lastname, phone, address, city, email, nhc, postal_code, birth_date, dni, gender FROM his.patient WHERE id = ?"//perform the Selectset tSC = ..Adapter.ExecuteQuery(.resultSet, sql, patientId)
If resultSet.Next() {
set personResult = {"id":(resultSet.GetData(1)), "name": (resultSet.GetData(2)),
"lastname": (resultSet.GetData(3)), "phone": (resultSet.GetData(4)),
"address": (resultSet.GetData(5)), "city": (resultSet.GetData(6)),
"email": (resultSet.GetData(7)), "nhc": (resultSet.GetData(8)),
"postalCode": (resultSet.GetData(9)), "birthDate": (resultSet.GetData(10)),
"dni": (resultSet.GetData(11)), "gender": (resultSet.GetData(12)), "type": ("Patient")}
} else {
set personResult = {}
}
//create the response messagedo patient.Resource.Insert(personResult.%ToJSON())
<span class="hljs-keyword">Return</span> tSC
}
ご覧のように、このメソッドは HIS のデータベースにクエリを発行し、すべての患者情報を取得してから後で String に変換されて Adapter.Message.FHIRResponse タイプの変数に格納される DynamicObject を生成します。 後でトレースでレスポンスを表示できるように、Resource プロパティを String リストとして定義しました。 直接 DynamicObjects として定義し、後続の変換を省略することもできます。
Bundle かどうかの確認:
BO からのレスポンスによって、Bundle タイプであるか(今後公開される記事で説明します)単なる Resource であるかをチェックします。
.png)
DynamicObject の作成:
BO レスポンスを DynamicObject に変換し、それを一時コンテキスト変数(context.temporalDO)に割り当てます。 変換に使用される関数は以下のとおりです。
##class(%DynamicAbstractObject).%FromJSON(context.FHIRObject.Resource.GetAt(1))FHIR の変換:
DynamicObject タイプの一時変数を使用して、クラス HS.FHIR.DTL.vR4.Model.Resource.Patient のオブジェクトへの変換を行います。 他のタイプのリソースを探す場合は、タイプごとに特定の変換を定義する必要があります。 では、変換を確認しましょう。
.png)
この変換によって、BS InteropService が解釈するオブジェクトが得られます。 結果を context.PatientResponse 変数に格納します。
Stream へのリソースの割り当て:
FHIR の変換で取得した変数 context.PatientResponse を Stream に変換します。
QuickStream への変換:
response 変数に、クライアントに戻す必要のあるすべてのデータを割り当てます。
set qs=##class(HS.SDA3.QuickStream).%New()
set response.QuickStreamId = qs.%Id()
set copyStatus = qs.CopyFrom(context.JSONPayloadStream)
set response.Response.ResponseFormatCode="JSON"set response.Response.Status=200set response.ContentType="application/fhir+json"set response.CharSet = "utf8"この場合、200 レスポンスを常に返します。 本番環境では、検索されたリソースが正しく取得されたことを確認し、取得されていない場合は、レスポンスのステータスを 200 から「見つかりません」に対応する 404 に変更します。 このコード箇所で見られるように、オブジェクト HS.FHIR.DTL.vR4.Model.Resource.Patient
は Stream に変換されて、 HS.SDA3.QuickStream として格納されます。そのオブジェクトの識別子を QuickStreamID 属性に追加されるため、その後 InteropService サービスによって結果を JSON として正しく戻せるようになります。
まとめ:
この記事で行ったことをまとめましょう。
- GET タイプのリクエストを送信して、定義された ID を持つ Patient リソースを検索しいました。
- BS InteropService は構成済みの BP にリクエストを転送しました。
- BP は、HIS データベースを操作する BO を呼び出しました。
- 構成済みの BO は、HIS データベースから患者データを取得しました。
- BP は結果を、デフォルトの InteropService が作成された BS によって理解可能なオブジェクトに変換しました。
- BS はレスポンスを受け取り、クライアントに転送しました。
ご覧のように、操作は比較的単純で、より多くのタイプのリソースをサーバーに追加する場合、BO に取得される新しいリソースに対応するデータベースのテーブルにクエリを追加し、BO の結果を対応する HS.FHIR.DTL.vR4.Model.Resource.* タイプのオブジェクトに変換する処理を BP に含めます。
次の記事では、Patient タイプの新しい FHIR リソースを HIS データベースに追加する方法を説明します。
お読みいただきありがとうございました!