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 |