вторник, 1 ноября 2016 г.

FHIR и XDS: отправка документа в репозиторий

Перевод оригинальной статьи Дэвида Хэя от 6 ноября 2013 года.

В данном посте мы уделим основное внимание такому действующему лицу как XDS Document Source ("источник документа"), механизму загрузки документа в репозиторий и регистрации его метаданных в XDS-инфраструктуре хранения ЭПМЗ. Рассмотрим некоторые аспекты реализации интеграционного профиля XDS в привязке к REST-архитектуре.
Метаданные о документе будем хранить в ресурсе DocumentReference; основную сложность для нас составит управление "идентификаторами сущностей" (enitity identifiers), т.е. идентификаторами пациентов, врачей и медорганизаций, как предписывает профиль XDS. В стандарте FHIR акцент смещен на идентификаторы ресурсов, которые используются для представления сущностей. Идентификаторы fhir-ресурсов используются для установления взаимосвязей между ресурсами.





Немного вводной информации

Идентификатор - это что-то, что позволяет выделить объект или сущность (например, пациента) из ряда ему подобных. Идентификаторы обладают набором свойств. В частности, нас интересуют два свойства идентификаторов - "система [идентификации или кодирования]" и "значение" идентификатора.
"Система" - это пространство имен, в котором значение идентификатора является уникальным. Одной сущности (например, пациенту) теоретически может быть назначено несколько идентификаторов, т.е. пациент может иметь:


  • Идентификатор национального уровня (например, паспорт или SSN)
  • Идентификатор в системе ведения медицинских записей врача
  • Номер водительского удостоверения
  • Библиотечную карточку и т.д.
Возможно, чтобы одной сущности (в том числе случайно) было назначено несколько (дублирующих) идентификаторов. Так, администратор может назначить пациенту новый идентификатор, забыв проверить наличие пациента в системе. [Следовательно, одной и той же сущности будет назначено 2 дублирующих идентификатора.] Поэтому, на сервис идентификации (identity service) возложена задача поиска дублирующих идентификаторов и их "слияние" (merge)...
При развертывании XDS-инфраструктуры организация IHE рекомендует участникам обмена заранее договориться об использовании "систем идентификации". [SSN в данном случае - есть система идентификации. "Серия + номер паспорта" также может быть "системой идентификации".] IHE также подготовила интеграционный профиль PIX (Перекрестные ссылки на идентификаторы пациента), который определяет механизм управления и перекрестного связывания идентификаторов. Суть такова: имея один идентификатор можно узнать другие идентификаторы интересующей нас сущности. Существует еще один профиль от IHE, названный "PDQ" (Запрос демографических данных пациента). Он описывает процесс отправки запроса в реестр документов. В PDQ-запросе указывается имя, дата рождения и другая демографическая информация пациента. В ответе на запрос возвращается список пациентов (удовлетворяющих поисковым критериям) с перечислением всех известных системе идентификаторов. Часто оба профиля реализуют одновременно (PIX/PDQ).
НО! Когда "источник документа" (Document Source) отправляет документ в репозиторий используя FHIR-интерфейс, ему требуется не идентификатор пациента (т.е. не идентификатор самой сущности), а ссылка на (представляющий данную сущность "пациент" в системе) ресурс Patient, т.е. uri ресурса Patient.
Проблема клиента в данном случае заключается в том, что у него в наличии есть только идентификатор пациента (т.е. идентификатор самой сущности).
Разбирая профиль PIX/PDQ, понимаешь, что подозрительно похожий механизм имеется и в FHIR - "опрашивание ресурса Patient" (query against patient resource). Представим, что у нас есть сервер c директорией /Patient (в посте это называется "end point" - конечная точка), также развернут механизм "слияния" дублирующих идентификаторов сущностей и обновления реестра идентификаторов. В таком случае, отправка запроса к данной конечной точке (/Patient) аналогична выполнению PIX/PDQ-запроса.


  • GET /Patient?name=power&gender=M      (PDQ-ish)
  • GET /Patient?identifier=PRP1660      (PIX-ish)
- оба запроса вернут список пациентов (отвечающих критериям поиска); для каждого пациента выдается список известных его идентификаторов. При необходимости по запросу нам могут быть выданы и более ранние версии ресурса; связь нескольких ресурсов Patient, которые представляют в системе одну и ту же сущность (т.е. дублирующих), определяется в элементе "link".
Итак, при условии, что наш сервер настроен именно так, как описано выше, (который в XDS-инфраструктуре выполняет роль "источника идентификаторов"), можно подумать, что вопрос с "поиском идентификаторов" решен. Ну или почти решен... Но на самом деле это не совсем так. Кто же должен заниматься поиском идентификаторов?
И у нас есть несколько вариантов:


  • Задачу можно переложить на репозиторий документов: пусть он будет получать идентификатор пациента (т.е. идентификатор сущности), отправлять запрос с этим id data-серверу, на котором хранятся ресурсы Patient, и получать в качестве ответа идентификатор ресурса (представляющего интересующую нас сущность). Можно даже реализовать этот механизм используя комплекты ресурсов FHIR (bundle) (кстати, использование бандлов в данном случае обязательно, т.к. они - неотъемлимая часть выполнения транзакций в fhir-основанной инфраструктуре). Данный подход, к сожалению, имеет одно ограничение - транзакция будет обрабатываться тем сервером, где выполнялся поиск, следовательно данные пациента должны дублированно храниться во всех (репозиториях). Очевидно, что это решение не является правильным.
  • Можно переложить обязанность поиска идентификаторов на "источник документов" (Document Source): он должен будет определить идентификатор и указать абсолютную ссылку на fhir-ресурс при загрузке документа на сервер репозитория.
Это накладывает на "источник документа" дополнительные обязательства: он должен гарантировать корректность идентификации пациента. По нашим ощущениям, это - правильно, поэтому, мы остановимся на этой схеме. На обновленной диаграмме видна стрелка между "источником документов" (Document Source) и "источником идентификаторов" (Identity Source), которая обозначает выполнение поиска.

Аналогичная схема работает и при поиске идентификаторов ресурсов, представляющих "врача" (practitioner), который также выполняет роль автора ЭПМЗ, и "медорганизацию" (organization), которая несет ответственность за обеспечение хранения документа. Ответственность за поиск идентификатора ресурса (а точнее, места где он хранится) все также несет "источник документа". С идентификацией ресурсов врача и медорганизации все обстоит немного проще - в инфраструктуре fhir основное внимание уделяется ресурсам Patient. Если нам не удалось узнать идентификатор этих ресурсов, мы всегда можем воспользоваться "фичей" fhir по включению неидентифицируемых ресурсов в ресурсе DocumentReference (т.е. делая их т.н. contained resources). Есть у этого подхода и недостатки, но, в некоторых случаях он будет единственным решением.


    Некоторые замечания:
  • Внимательные читатели заметят, что в поле "subject" ресурса DocumentReference может заполняться ссылкой не только на ресурсы Patient. В этом поле "источник документа" (Document Source) должен указывать идентификатор ресурса (а не идентифицируемой сущности).
  • Можно развернуть (SOAP или REST) ​веб-сервис ​для репозитория, который будет выполнять поиск идентификатора, т.е. будет использовать идентификатор сущности для отправки запроса на соответствующий сервер от имени "источника документа". У этого подхода есть свои особенности, на которые нужно обратить внимание.
  • Можно хранить дубликаты ресурсов Patient в репозитории и использовать поиск комплектов (bundle search), в результате которого будут сгенерированы локальные идентификаторы.

Итак, мы развели по разные стороны идентификаторы сущностей и идентификаторы ресурсов, поэтому перейдем к загрузке документа в хранилище. При загрузке нам потребуется внести изменения в репозиторий и в реестр документов (изменения будут частью одной транзакции). Это можно сделать несколькими способами:



  • "Источник документов" (Document Source) может отправить документ в хранилище, получить сгенерированный для него идентификатор (или каким-либо другим образом узнать назначенный ему URI. По этому URI "заказчик документа" сможет запросить документ из хранилища), далее создав и отправив ресурс DocumentReference в реестр документов. Это вполне работающий метод (его обязательно использовать, если хранилище не имеет fhir-интерфейса), который, однако, подразумевает, что контроль за выполнением транзакции осуществляется клиентом. В некоторых ситуациях это может быть недопустимо.
  • Используя возможности FHIR по обработке транзакций, мы могли бы переложить ответственность на сервер репозитория (repository server).

Далее мы будем рассматривать второй подход, которой будет состоять из следующих шагов:



  1. Создание нового комплекта ресурсов (bundle);
  2. Кодирование документа, который мы хотим загрузить в репозиторий, в формате base64, добавление его в комплект ресурсов в виде ресурса Binary
  3. Создание ресурса DocumentReference, добавление его в уже созданный комплект (bundle)
  4. Загрузка комплекта ресурсов в корневой каталог репозитория
  5. Обработка ответа репозитория (выполнено успешно или с ошибкой)


1. Создание нового комплекта ресурсов (bundle)

Все сводится к созданию базовой структуры комплекта ресурсов.

2. Извлечем документ из нашего локального хранилища, закодируем его в формате base64, добавим его в комплект в формате ресурса Binary.

Идентификатор ресурса Binary должен соответствовать формату cid, чтобы сервер репозитория создавал новый ресурс (т.е. обрабатывал загружаемый на него комплект как триггер на выполнение команды create).

3. Создадим ресурс DocumentReference

Ресурс DocumentReference содержит метаданные документа, необходимые для его поиска (по запросу); содержимое DocumentReference зависит от наличия данных, а также договоренностей участников affinity domain (т.е. договоренностей о том, какие значения полей ресурса DocumentReference допустимы).
Чтобы выполнить требования XDS профилем наложили дополнительные ограничения на "стандартный" ресурс DocumentReference.
Ресурс DocumentReference содержит ссылки на другие нужные нам ресурсы, например:


  • Прямая ссылка на ресурс на сервере (например, на ресурс Patient). Нет необходимости включать этот ресурс в комплект (bundle), хотя этого никто не запрещает.
  • Ресурсы, uri которых определить не удается, могут стать вложенными ресурсами (contained resource).
Добавим ресурс DocumentReference в бандл. (В бандл можно включать и другие ресурсы, такие как вложения к документам.)

4. Отправка комплекта ресурсов в корневой каталог репозитория и ожидание ответа

Методом POST отправим комплект (bundle) в корневую директорию сервера репозитория (как это сделать описано в посте про медицинские препараты). Сервер сам сделает всю работу, которая будет состоять из следующих шагов:


  1. Сохранение докумена в локальном хранилище, в результате чего будет сгенерирован новый идентификатор. Позже документ будет доступен по запросу к директории сервера /Binary (т.е. выполнения запроса вроде - GET/Binary/ {ID})
  2. Замещение значения поля "location" ресурса DocumentReference на локальный uri
  3. Отправка методом POST ресурса DocumentReference в реестр документов
  4. Возвращение ответа клиенту

Обратите внимание: мы подразумевает, что контроль за выполнением транзакций осуществляется репозиторием. Если репозиторий возвращает ошибку (например, отсутствуют данные или были предоставлены некорректные данные), он должен самостоятельно удалить документ из локального хранилища и вернуть клиенту ошибку (в самом лучшем случае также он должен вернуть ресурс OperationOutcome).

Описанный выше механизм (т.е. когда сервер отвечает за обработку комплектов, содержащих ресурсы DocumentReference и Binary, сам отправляет методом POST ресурс DocumentReference в настроенный сервер реестра документов) зависит от локальных политик и настроек конфигурации. Возможно, лучше будет создать "кастомную" конечную точку (наверное, имеется ввиду директория на сервере), чтобы процесс обработки был более явным. Есть и другие варианты обработки бандлов сервером (например, асинхронная обработка).

5. Обработка ответа сервера

Полученный HTTP-код состояния сообщит об успешной или неудачной обработке транзакции. В случае ошибки, желательно, чтобы сервер возвращал ресурс OperationOutcome, содержащий подробную информацию о транзакции.
В итоге мы имеем загруженный в репозиторий документ и обновленный реестр документов.

Комментариев нет:

Отправить комментарий