Перейти к содержанию

C4-диаграммы

Четыре уровня детализации: контекст → контейнеры → компоненты «Триажа» → код/данные (последний покрыт схемой БД).

Level 1 — Системный контекст

Кто и с чем взаимодействует. Границы ПАК — внутри пунктира.

flowchart TB
  subgraph EXT["Внешние системы"]
    SMP["ИС «МРМ бригады СМП»<br/>сервер станции СМП"]
    GEO["Серверы координат<br/>GPS-сервер вендора /<br/>сервер маршрутизации"]
    CISZ["ЦИСЗ<br/>централизованная ИС<br/>здравоохранения РБ"]
  end

  subgraph UZ["Контур УЗ «БЦГБ»"]
    subgraph PAK["ПАК 3H (разрабатывается)"]
      TRIAGE["Подсистема «Триаж»"]
    end
    MIS["МИС АИС «Медик»<br/><i>имеется</i>"]
  end

  subgraph USERS["Пользователи"]
    OP["Оператор<br/>приёмного отделения"]
    PAT["Пациент"]
  end

  SMP -->|"FHIR Bundle, push<br/>сервер-сервер, mTLS"| TRIAGE
  TRIAGE -->|"опрос координат<br/>по расписанию"| GEO
  TRIAGE <-->|"обращение, бригада встречи"| MIS
  MIS <-->|"интеграционная шина<br/>Минздрава"| CISZ

  OP -->|"пульт, сортировка,<br/>оформление"| TRIAGE
  PAT -->|"терминал<br/>самообращения"| TRIAGE

Читать вместе с ADR-9

Ни одна подсистема ПАК не соединяется с ЦИСЗ напрямую — единственный путь наружу лежит через МИС. См. ADR-9.

Level 2 — Контейнеры

Что из чего состоит и как разворачивается.

flowchart TB
  subgraph EDGE["Периметр"]
    NGINX["nginx<br/>TLS/mTLS, reverse proxy,<br/>IP allowlist"]
  end

  subgraph APP["Прикладные сервисы"]
    GW["fhir-gateway<br/>NestJS<br/>приём 5 типов сообщений"]
    CORE["triage-core<br/>NestJS<br/>направления, очередь, талоны"]
    GEOW["geo-worker<br/>@nestjs/schedule<br/>опрос координат"]
    RULES["rules-engine<br/>оценка состояния → цвет"]
    MISAD["mis-adapter<br/>обращения, бригады"]
  end

  subgraph DATA["Хранение"]
    PG[("PostgreSQL 16<br/>бизнес-данные, журналы,<br/>партиции по времени")]
    MQ["RabbitMQ<br/>буфер событий"]
  end

  subgraph UI["Клиенты"]
    PULT["Центральный пульт<br/>Angular + WebSocket"]
    KIOSK["Терминал самообращения"]
  end

  NGINX --> GW & CORE
  GW --> MQ --> CORE
  CORE --> RULES
  CORE <--> MISAD
  GEOW --> PG
  GW & CORE & MISAD --> PG
  CORE -->|WebSocket| PULT
  CORE --> KIOSK
Контейнер Ответственность Почему отдельно
fhir-gateway Приём и валидация входящих FHIR-сообщений, идемпотентность, запись сырого тела Точка контакта с внешним миром: изолирована, чтобы всплеск или атака не задели ядро
triage-core Направления, обращения, очередь, талоны, сортировка, оформление Ядро бизнес-логики «Триажа»
geo-worker Опрос серверов координат по расписанию Отказ GPS-сервера не должен влиять на приём медданных (ADR-6)
rules-engine Интерпретация правил сортировки, версионирование Правила меняет медперсонал без релиза (ADR-7)

| mis-adapter | Единственная точка общения с МИС | Смена вендора МИС затрагивает один контейнер |

Level 3 — Компоненты подсистемы «Триаж»

flowchart TB
  subgraph GW["fhir-gateway"]
    LIS["Listener<br/>HTTP/mTLS на выделенном порту"]
    VAL["FHIR-валидатор<br/>JSON Schema R4"]
    IDEM["Контроль идемпотентности<br/>provider + message_uid"]
    ACL["ACL-адаптеры вендоров<br/>МРМ СМП · Эрикполь · МАП · Лекарь"]
    RAW["Запись сырого Bundle → JSONB"]
  end

  subgraph CORE["triage-core"]
    DISP["Диспетчер типов сообщений"]
    BED["Обработчик запроса мест<br/>transport_request"]
    NOTIF["Регистратор направления<br/>transport_notification"]
    HEALTH["Приёмник состояния<br/>transport_health_status"]
    CANC["Обработчик отмены<br/>transport_cancel"]
    COMP["Регистратор доставки<br/>transport_complete"]
    APPEAL["Менеджер обращений<br/>СМП + самообращения"]
    QUEUE["Очередь и талоны<br/>К43 / В34 / П45"]
    SLA["Контроль нормативного времени"]
    HANDOFF["Ручная передача в МИС"]
  end

  LIS --> VAL --> IDEM --> ACL --> RAW
  ACL --> DISP
  DISP --> BED & NOTIF & HEALTH & CANC & COMP
  NOTIF --> APPEAL
  COMP --> APPEAL
  APPEAL --> QUEUE --> SLA
  APPEAL --> HANDOFF

Поток одного входящего сообщения

sequenceDiagram
  autonumber
  participant SMP as Сервер СМП
  participant NG as nginx (mTLS)
  participant GW as fhir-gateway
  participant DB as PostgreSQL
  participant MQ as RabbitMQ
  participant CORE as triage-core
  participant UI as Пульт

  SMP->>NG: POST /fhir/$process-message (Bundle)
  NG->>NG: проверка клиентского сертификата + IP allowlist
  NG->>GW: проксирование
  GW->>GW: валидация FHIR R4
  GW->>DB: запись сырого Bundle (JSONB) + inbound_messages
  GW->>DB: проверка (provider_id, message_uid)
  alt дубликат
    GW-->>SMP: 200 OK (тот же результат, без побочных эффектов)
  else новое сообщение
    GW->>MQ: publish событие
    GW-->>SMP: 202 Accepted
    MQ->>CORE: доставка
    CORE->>DB: обновление направления / состояния
    CORE->>UI: push по WebSocket
  end

Почему 202, а не 200

Шлюз подтверждает приём, а не выполнение бизнес-логики. Это позволяет ответить вендору быстро и не держать его соединение на время обработки. Единственное исключение — transport_request (запрос мест), где вендор ждёт содержательный ответ синхронно; там ответ формируется в запросе. См. «Контракты».

Level 3 — Компоненты «Триажа» (продолжение)

Соответствие компонентов требованиям

Компонент Закрывает
Listener + FHIR-валидатор + ACL FR-T-1, FR-T-2, FR-T-8
Контроль идемпотентности ADR-3
Обработчик запроса мест FR-T-3
Регистратор направления FR-T-4
Приёмник состояния FR-T-5
geo-worker FR-T-6, FR-T-7
Менеджер обращений FR-T-13, FR-T-14, FR-T-22
Очередь и талоны FR-T-15, FR-T-16, FR-T-20
Контроль нормативного времени FR-T-21
rules-engine FR-T-17, FR-T-18, FR-T-25
Ручная передача в МИС FR-T-11
mis-adapter FR-T-9, FR-T-23, FR-M-6