ADR-006. Структура данных информационной карты

ADR Версия: 0.5 accepted

Как хранить содержимое информационной карты: фиксированными свойствами, иерархией видов или шаблоном вида ИК.

⟨/⟩ Исходник

1. Контекст

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

Что известно из образцов заказчика (см. «Образцы бумажных информационных карт»):

Ключевое ограничение: Сервис оцифровывает уже существующий архив. Любая схема, которая не даёт внести карту, лежащую в фонде, неприемлема — исправить бумагу нельзя.

2. Рассматриваемые варианты

2.1. Вариант 1. Фиксированные свойства сущности

Каждое поле формы — свойство класса информационной карты, с собственным типом.

За: типизация и валидация на уровне модели, поиск и фильтрация по полю средствами хранилища, ошибка в имени поля ловится компилятором.

Против: класс становится объединением полей всех видов ИК, где для конкретной карты большая часть свойств заведомо пуста и неизвестно, какая именно. Каждая новая редакция бланка и каждый новый вид документа требуют правки кода и миграции. Различие в подписи одного и того же поля между редакциями выразить нечем — придётся выбрать одно написание и потерять остальные.

2.2. Вариант 2. Иерархия видов ИК (наследование классов)

Базовый класс с общей частью, наследники по видам: ИК издания, ИК рукописи и далее.

За: расхождения между видами выражены явно, общая часть описана один раз, тип карты известен статически.

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

2.3. Вариант 3. Плоский список полей (как сейчас)

Карта хранит список значений «раздел + наименование поля + значение».

За: любой вид ИК и любая редакция укладываются без изменения схемы; экраны проверки и выгрузка пишутся один раз на все виды.

Против: состав полей ничем не задан и не контролируется — ничто не мешает записать Заглавие и заглавие как разные поля; нельзя ответить на вопрос, какие поля вид ИК обязан иметь; типизации нет, дата и число хранятся строками.

2.4. Вариант 4. Раздельное описание электронного документа и бумажной формы

Состав полей описывается данными, а не кодом. Ключевое отличие от вариантов 1–3: что хранится и как это напечатано — два разных описания, а не одно.

2.4.1. Электронный документ: что хранится

Конфигурация документа — состав: перечень атрибутов с обязательностью и допустимые вложенные документы с их кратностью. Отвечает на вопрос «из чего состоит ИК издания», без единого слова о том, как это напечатано.

Атрибут — поле как понятие: код, каноническое наименование, тип значения. Живёт в общем словаре и переиспользуется всеми конфигурациями.

Тип значения — правило, которому подчиняется значение: вид значения и ограничения.

вид = string;  min = 1;   max = 500      → длина значения
вид = number;  min = 0;   max = 14       → диапазон значения
вид = date;    min = 900; max = 2026     → диапазон значения
вид = bool;                                  → отметка в клетке: есть или нет
вид = choice [A: «Слепое»; B: «Золотое»; C: «Другие виды»];  min = 0; max = 3
вид = ref → справочник «Материалы»;                          min = 0; max = 1

Пара min/max есть у любого вида, но означает осмысленное именно для него: у строки — длину, у числа и даты — границы, у выбора — количество выбранных значений. Этим же задаётся кратность: max = 1 — выбор одного (степень повреждения), max = N — множественный (материалы основы, украшения).

Для одиночной отметки в клетке заведён отдельный вид bool: выражать её выбором из набора в одно значение можно, но читается это хуже, а таких полей на форме больше всего.

choice перечисляет значения прямо в типе, ref ссылается на справочник. Критерий: если один и тот же набор значений нужен двум и более атрибутам — это справочник.

Документ — экземпляр: docId равен собственному идентификатору, parentId пуст у корня; ссылка на конфигурацию; значения атрибутов. Документ может быть составным: у вложенного документа parentId указывает на непосредственного родителя, а docId — на корень, поэтому весь документ достаётся одним запросом.

Корневой документ — агрегат: части адресуются только через него.

Вложенный документ — эксперты, замеры pH, авторы, переписчики, владельцы, биотесты. Это состав, а не вёрстка: список существует в данных, даже если на бланке под него отведена одна строка.

Значение атрибута — ссылка на атрибут и одно или несколько значений.

2.4.2. Бумажный документ: как это напечатано

Форма документа — описание бланка целиком; ссылается на конфигурацию документа. У одной конфигурации может быть несколько форм — по числу редакций бланка в обращении.

Страница формы — номер, заголовок, роль-заполнитель.

Раздел — заголовок, порядок; разделы вкладываются друг в друга.

Размещение атрибута — атрибут, подпись именно на этом бланке, место на листе. Область поля на изображении относится к форме, а не к конфигурации: конфигурация описывает состав, а не бумагу.

Версий у формы нет: изменился бланк — это другая форма. Версионирование не нужно потому, что значение опознаётся кодом атрибута и от места печати не зависит.

Страницы, разделы, подписи и координаты к электронному документу отношения не имеют.

2.4.3. Связь двух описаний

Форма ссылается на конфигурацию. В форму попадают не все атрибуты состава — служебные данные Сервиса на бумаге отсутствуют, — но атрибута, которого нет в составе, в форме быть не может. Обязательность — свойство состава, а не формы.

То, с какой формы оцифрована конкретная карта, — свойство пакета ввода: это факт ввода, а не свойство самой карты. Без него нельзя объяснить, почему поле распозналось так, а не иначе.

2.4.4. Ядро карты: типизированные свойства поверх состава

Информационная карта остаётся отдельной сущностью, а не растворяется в общем документе. У неё есть то, чего у абстрактного документа быть не может: связь с документом фонда, устанавливаемая на проверке экземпляров; связь с пакетом-первоисточником; дата карты как часть идентификации; факт выгрузки в АБИС; собственный жизненный цикл — карта создаётся командой пользователя по итогам проверки. Загнать это в атрибуты нельзя: «выгружена ли карта» не может быть строкой в общем хранилище значений.

Сверх этого карта несёт ядро — небольшой набор типизированных свойств: шифр, инвентарный номер, дата, заглавие, автор, год.

Критерий отбора в ядро — функциональный, а не структурный. В ядро идёт не то, что встречается на всех бланках, а то, по чему Сервис работает сам: ищет карту, строит реестр, отбирает выборку, подбирает экземпляр фонда, формирует выгрузку. Структурный критерий «общее для всех конфигураций» здесь не работает: у ИК издания одно поле «Заглавие», у ИК рукописи их три — унифицированное, полное и перевод.

Ядро — проекция, а не второй источник истины. Источник истины — значения атрибутов; свойства ядра заполняются из них и пересчитываются при правке значений. Какой атрибут даёт какое свойство ядра, задаёт конфигурация: у ИК рукописи заглавие берётся из унифицированного заглавия, и оно же, по-видимому, соответствует полю «Заглавие» на форме на издание.

Что это даёт: поиск и отбор по ядру идут по типизированным колонкам, то есть недостаток варианта 4 — «поиск через связь значения с атрибутом» — снимается для тех полей, по которым действительно ищут. Вопрос остаётся только для поиска за пределами ядра (OQ-A6-4).

Цена: правило отображения «атрибут → свойство ядра» для каждой конфигурации и обязанность пересчитывать ядро при изменении значений. Рассинхронизация ядра и значений — то, что здесь может сломаться.

2.4.5. Принятые решения

2.4.6. Соглашения по переносу с бумаги

2.4.7. За

2.4.8. Против

3. Вариант 5. Вид документа и атрибут документа карты

Раскладка, предложенная после принятия варианта 4. Отличается от него тремя вещами: видом документа вместо конфигурации, деревом видов наряду с деревом экземпляров и отдельной сущностью «атрибут документа карты» между составом и значением.

classDiagram
  direction LR

  class AttributeValueType["Тип значения"] {
    <<Entity>>
    +Guid Id
    +String Code
    +String Name
    +ValueKind Kind
    +String Constraints
  }

  class DocumentKind["Вид документа"] {
    <<Entity>>
    +Guid DocId
    +Guid ParentId
    +String DocKind
    +String Name
    +IReadOnlyList~DocumentKind~ NestedKinds
    +IReadOnlyList~DocumentKindAttribute~ Attributes
  }

  class DocumentKindAttribute["Атрибут вида документа"] {
    <<Entity>>
    +DocumentKind DocumentKind
    +String Code
    +String Name
    +AttributeValueType ValueType
    +Boolean IsRequired
    +Int32 Order
    +CardCoreField CoreField
  }

  class InformationCard["Информационная карта"] {
    <<Entity>>
    +Guid Id
    +DocumentKind Kind
    +DateOnly Date
    +IReadOnlyList~CardDocument~ Documents
  }

  class CardDocument["Документ информационной карты"] {
    <<Entity>>
    +Guid DocId
    +Guid ParentId
    +DocumentKind Kind
    +IReadOnlyList~CardDocument~ NestedDocuments
    +IReadOnlyList~CardDocumentAttribute~ Attributes
  }

  class CardDocumentAttribute["Атрибут документа карты"] {
    <<Entity>>
    +CardDocument Document
    +DocumentKindAttribute KindAttribute
    +IReadOnlyList~String~ Values
    +ValueOrigin Origin
    +Guid FilledByUserId
    +DateTime FilledAt
  }

  class DocumentForm["Форма документа"] {
    <<Entity>>
    +Guid Id
    +DocumentKind DocumentKind
    +String Name
    +IReadOnlyList~FormPage~ Pages
  }

  class FormPage["Страница формы"] {
    <<Entity>>
    +Int32 Number
    +String Title
    +IReadOnlyList~FormSection~ Sections
  }

  class FormSection["Раздел страницы"] {
    <<Entity>>
    +String Title
    +Int32 Order
    +IReadOnlyList~FormSection~ NestedSections
    +IReadOnlyList~FormField~ Fields
  }

  class FormField["Поле формы"] {
    <<Entity>>
    +DocumentKindAttribute KindAttribute
    +String Caption
    +Int32 Order
    +SourceArea Area
  }

  class SourceArea["Область на кадре"] {
    <<ValueObject>>
    +Double X
    +Double Y
    +Double Width
    +Double Height
  }

  class InputPacket["Пакет ввода"] {
    <<Entity>>
    +Guid Id
    +String Number
    +DocumentForm DocumentForm
    +DateTime QueuedAt
    +Guid CreatedByUserId
    +String RecognitionRequestJson
    +String RecognitionResultJson
    +DateTime RecognizedAt
    +IReadOnlyList~ImageFrame~ Frames
    +IReadOnlyList~PacketFieldValue~ Values
  }

  class ImageFrame["Кадр"] {
    <<Entity>>
    +Guid Id
    +Int32 Order
    +String StorageKey
    +ImageSource Source
    +FormPage FormPage
  }

  class PacketFieldValue["Значение поля пакета"] {
    <<Entity>>
    +DocumentKindAttribute KindAttribute
    +FormField FormField
    +IReadOnlyList~String~ Values
    +IReadOnlyList~String~ RecognizedValues
    +ValueOrigin Origin
    +Double Confidence
    +ImageFrame ImageFrame
    +SourceArea Area
  }

  DocumentKind "1" *-- "0..*" DocumentKind : ParentId
  DocumentKind "1" *-- "0..*" DocumentKindAttribute : Attributes
  DocumentKindAttribute --> "1" AttributeValueType : ValueType

  DocumentForm --> "1" DocumentKind : DocumentKind
  DocumentForm "1" *-- "1..*" FormPage : Pages
  FormPage "1" *-- "0..*" FormSection : Sections
  FormSection "1" *-- "0..*" FormSection : Sections
  FormSection "1" *-- "0..*" FormField : Fields
  FormField --> "1" DocumentKindAttribute : KindAttribute


  InputPacket --> "1" DocumentForm : DocumentForm
  InputPacket "1" *-- "1..*" ImageFrame : Frames
  InputPacket "1" *-- "0..*" PacketFieldValue : Values
  ImageFrame --> "0..1" FormPage : FormPage
  PacketFieldValue --> "1" DocumentKindAttribute : KindAttribute
  PacketFieldValue --> "0..1" FormField : FormField
  PacketFieldValue --> "0..1" ImageFrame : ImageFrame
  PacketFieldValue "1" *-- "0..1" SourceArea : Area
  FormField "1" *-- "0..1" SourceArea : Area

  InformationCard --> "1" InputPacket : SourcePacket
  InformationCard --> "1" DocumentKind : Kind
  InformationCard "1" *-- "1..*" CardDocument : Documents

  CardDocument --> "1" DocumentKind : Kind
  CardDocument "1" *-- "0..*" CardDocument : ParentId
  CardDocument "1" *-- "0..*" CardDocumentAttribute : Attributes

  CardDocumentAttribute --> "1" DocumentKindAttribute : KindAttribute

  style DocumentKind fill:#d6e4f7,stroke:#2f5f96
  style DocumentKindAttribute fill:#d6e4f7,stroke:#2f5f96
  style DocumentForm fill:#dcefdc,stroke:#3f7f3f
  style FormPage fill:#dcefdc,stroke:#3f7f3f
  style FormSection fill:#dcefdc,stroke:#3f7f3f
  style FormField fill:#dcefdc,stroke:#3f7f3f
  style InputPacket fill:#fbe6cd,stroke:#b5762a
  style ImageFrame fill:#fbe6cd,stroke:#b5762a
  style PacketFieldValue fill:#fbe6cd,stroke:#b5762a
  style InformationCard fill:#f7dfe4,stroke:#a34457
  style CardDocument fill:#f7dfe4,stroke:#a34457
  style CardDocumentAttribute fill:#f7dfe4,stroke:#a34457
  style AttributeValueType fill:#f2f2f2,stroke:#8c8c8c
  style SourceArea fill:#f2f2f2,stroke:#8c8c8c

Цветом выделены четыре агрегата: вид документа — голубой, форма документа — зелёный, пакет ввода — оранжевый, информационная карта — розовый. Серым — то, что вне агрегатов: типы значений и область на кадре.

3.1. Как читается цепочка

Состав задаётся дважды параллельными парами. Вид документа и его атрибут вида — описание того, что бывает; документ карты и его атрибут документа — то, что заполнено на конкретной карте. Каждый уровень экземпляра ссылается на свой уровень описания: документ карты знает свой вид, атрибут документа карты знает атрибут вида.

Значение — поле атрибута документа карты, а не отдельная сущность: сначала «в этом документе есть такое поле», затем «у поля такие значения».

Отдельную сущность значения заводить не за чем: собственных реквизитов у неё нет — уверенность, источник и область остались в пакете ввода, потому что это данные о распознавании, а не о результате. Такая сущность была бы полем, записанным отдельной таблицей, и добавила бы третий переход на пути от документа к данным там, где второй уже введён осознанно — ради факта заполнения.

Множественные значения (материалы основы, украшения, физико-механические повреждения отмечаются несколькими клетками сразу) хранятся списком в том же поле. Цена: запрос «во всех картах, где среди материалов есть картон» становится поиском по содержимому списка, а не соединением таблиц, — и требует индекса соответствующего вида. Это приемлемо, потому что поиск за пределами ядра решено не поддерживать.

3.2. Бумажная форма

Форма документа — форма конкретного вида документа; у одного вида форм может быть несколько, по числу редакций бланка в обращении. Форма содержит страницы, страница — разделы, разделы вкладываются друг в друга. Поле формы входит в раздел, несёт подпись, как она напечатана на этом бланке, и связано с атрибутом вида документа — то есть с полем в составе конкретного вида.

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

Явная связь формы с видом при этом не лишняя, хотя вид выводится и из полей. Она отвечает на вопрос «какие формы есть у этого вида» без обхода всех полей, позволяет завести форму до наполнения её полями и делает проверяемым инвариант: все поля формы ссылаются на атрибуты того же вида, что указан у формы.

Обратная сторона: если один и тот же атрибут входит в состав двух видов документа — например, дата и в карту, и в лист экспертизы, — это два разных атрибута вида, и форма обязана ссылаться на нужный. Ошибка тут возможна и обнаружится только при сверке с бумагой.

3.3. Ввод и распознавание

Пакет ввода собирается по форме документа: бланк известен заранее, значит известен и ожидаемый состав атрибутов. Поэтому отдельной сущности «идентификатор пакета» нет — шифр, инвентарный номер, место хранения и дата это обычные атрибуты вида документа, и их значения хранятся наравне с остальными.

Из этого следует устройство значения: оно ссылается на атрибут вида документа обязательно, а на поле формы — только если атрибут на бланке напечатан. Место хранения поля формы не имеет: его неоткуда распознать, оно всегда вводится вручную. Ссылка на поле нужна для показа — подпись, место, подсветка фрагмента.

Заполнил пользователь значение при формировании пакета или при проверке — разницы нет, это одно и то же значение одного и того же атрибута; различается только происхождение.

Кадр относится к странице формы, а не к «роли изображения»: страница 3 формы на рукопись точнее, чем абстрактная роль. Ссылка необязательна: кадр, который обработка ни к одной странице не отнесла, считается лишним и не распознаётся.

Запрос к модели и ответ хранятся полями пакета. Пакет сериализуется в JSON по структуре формы, ответ модели сохраняется в пакет и разбирается в значения; интерфейс рендерит их обратно на структуру формы — страницы, разделы, подписи полей. Повторная обработка переписывает результат прежней: история прогонов пока не нужна.

Ключом в JSON служит код атрибута, а не подпись поля: подпись меняется от бланка к бланку, и по ней результат обратно не разложить. Форма задаёт структуру запроса и порядок, состав вида — имена.

Исправление пользователя не затирает распознанное. У значения два набора: текущий и то, что вернула модель. Причина не в аудите — пары «что модель прочла / что там на самом деле» и есть материал для дообучения, который Сервис накапливает попутно. Если исправление затирает распознанное, материал теряется навсегда.

Источник значения — свойства самого значения: кадр и область, оба необязательные. У введённого вручную источника нет, у распознанного из клетки есть и кадр, и область, у отнесённого к странице целиком — кадр без области. Отдельная сущность понадобилась бы, если бы одно значение собиралось из нескольких мест; на бланке такого не бывает.

3.4. Атрибут вида документа и связующие сущности

Атрибут вида документа — самостоятельное понятие, а не связка. Он и есть поле в составе вида: код, каноническое наименование, тип значения, обязательность, порядок, отображение в ядро карты. Общего словаря атрибутов над видами нет — атрибут заводится в виде и принадлежит ему.

Ещё две сущности существуют ради связи, и вся их ценность — в том, что на этой связи можно хранить. Без реквизитов каждая вырождается в лишнюю таблицу.

Связующая сущность Что связывает Что несёт
Поле формы раздел страницы ↔ атрибут вида документа подпись на этом бланке, порядок в разделе, область на странице
Атрибут документа карты документ карты ↔ атрибут вида документа значения, происхождение (распознано или введено), кем и когда заполнено

Разнесены они не случайно: одно и то же понятие в трёх ролях требует разных реквизитов. Обязательность поля не зависит от бланка, поэтому живёт в составе. Подпись и место зависят только от бланка, поэтому живут в форме. Кем и когда заполнено — факт конкретной карты, поэтому живёт в документе карты.

3.5. Почему словаря атрибутов нет

Общий словарь давал переиспользование: одно поле, вошедшее в состав двух видов. Вместе с ним он давал и общие ограничения — правка диапазона ради одного вида молча меняла поведение остальных, и заметить это было неоткуда: обратной связи «где ещё используется» в модели нет.

Переиспользование переехало на тип значения. Тип именован, заводится осознанно и выбирается при описании атрибута: «степень повреждения» — один тип на шесть полей листа сохранности. Общее осталось общим там, где это решение, а не побочное следствие того, что два вида сослались на одну запись словаря.

Цена: код атрибута уникален внутри вида, а не на всю систему. Сравнимость карт разных видов от этого не страдает — её обеспечивает ядро карты, где шифр издания и шифр рукописи сводятся в одну колонку. Выгрузка в АБИС тоже задаётся по видам: поля формата для издания и рукописи разные, одного правила на все виды всё равно не выходило.

Взамен появляется то, чего не было: виды изолированы. Правка состава ради одного вида другой задеть не может, потому что общего между ними не осталось.

Это и есть главный выигрыш варианта 5 против варианта 4: там значение ссылалось прямо на атрибут, и вешать на него «кем заполнено» было некуда — пришлось бы либо раздувать само значение, либо хранить сведения о заполнении на документе целиком, теряя привязку к полю.

3.6. Отличия от варианта 4

Вариант 4 Вариант 5
Конфигурация документа Вид документа с признаком DocKind
Виды документов не связаны между собой Вид документа входит в состав вида — дерево описаний
Значение ссылается прямо на атрибут Между ними появился атрибут документа карты; значение — его поле
Карта содержит один корневой документ Карта содержит документы карты, каждый своего вида
Экземпляр фонда Экземпляр фонда
Дата карты — часть ядра Дата вынесена как обязательный реквизит карты: дата передачи экземпляра в обработку

3.7. Что даёт отдельный атрибут документа карты

Появляется место, где живёт факт заполнения, отдельный и от описания, и от значения. Туда естественно ложится то, для чего в варианте 4 места не было: кем и когда заполнено, распознано или введено вручную, подтверждено ли пользователем. В варианте 4 это пришлось бы вешать либо на значение, либо на документ целиком.

Цена — лишний уровень косвенности: чтобы добраться от документа до значения, нужно пройти через две сущности вместо одной, и это же удваивает число записей при хранении.

3.8. Что даёт дерево видов

Вид документа, входящий в состав вида, позволяет описать вложение на уровне описания: «в ИК издания входит эксперт, а в эксперта — ничего». В варианте 4 то же выражено списком допустимых вложенных конфигураций с кратностью.

Разница в том, что дерево видов не хранит кратность: сказать «экспертов может быть сколько угодно, а лист сохранности ровно один» этой связью нельзя, если не добавить границы отдельно.

3.9. Вопросы к варианту

Вопрос
1 Карта «является документом вида» и при этом содержит документы карты. Карта — корневой документ дерева или отдельная сущность над деревом? От этого зависит, есть ли у неё собственные атрибуты
2 Кратность вложения: где хранится «сколько экземпляров вложенного вида допустимо», если дерево видов её не несёт
3 Нужен ли DocKind при наличии дерева видов — что он различает сверх самого вида
4 Экземпляр фонда вместо экземпляра фонда: это переименование или другая сущность (экземпляр против издания)
5 Дата карты — дата передачи экземпляра в обработку. На бумажной форме в поле «Дата» пишут именно её или дату заполнения карты
6 Что происходит с исправлениями пользователя при повторной обработке: машина меняет только незатронутые значения или переписывает все
7 Может ли форма размещать поля вложенных видов — например, печатать эксперта прямо на листе карты, — или у каждого вида своя форма

4. Решение

Принят вариант 5. Состав информационной карты описывается данными, причём что хранится и как это напечатано — два раздельных описания; между описанием и значением стоит факт заполнения.

Чем вариант 5 отличается от принятого прежде варианта 4 и что даёт взамен — в разборе варианта выше. Коротко: появилось место для факта заполнения, которого в варианте 4 не было, и связь поля формы с атрибутом в составе конкретного вида вместо инварианта, который приходилось проверять.

Варианты 1–3 отклонены. Фиксированные свойства и наследование классов не выдерживают расхождения видов ИК и редакций бланка: по образцам заказчика формы на издание и на рукопись почти не пересекаются по составу, а редакций бланка на издание уже три. Плоский список полей допускает произвольный состав и не даёт ни контроля, ни типов.

Решающий довод — свойство задачи, а не удобство реализации: Сервис оцифровывает уже существующий архив. Схема, не позволяющая внести карту, которая лежит в фонде, неприемлема, потому что исправить бумагу нельзя.

5. Рукописный текст вне клеток

На бланках встречается текст, не помещающийся ни в одно поле: приписка «нет» справа от строки степени повреждения, уточнение «перед форзацем» рядом со значением pH. Терять его нельзя — это сведения о памятнике, внесённые хранителем.

Предлагаемое решение: примечание — такой же вложенный вид документа, как эксперт. В состав вида ИК входит вид «примечание» с атрибутом текста; на каждый распознанный кусок рукописного текста заводится отдельный вложенный документ. Числа их заранее не знает никто — и не должен: вложение не ограничено по количеству.

Кадр и область при этом не нужны примечанию как таковому: это свойства значения в пакете ввода, где у распознанного текста уже есть и кадр, и прямоугольник. Модель распознавания возвращает такие куски отдельным списком (notes в ответе), и разбор превращает каждый в примечание.

Что это даёт: примечание можно при проверке превратить в значение атрибута — пользователь указывает, к какому полю относится текст, — и наоборот. Отдельной механики для этого не нужно, обе стороны суть документы одной модели.

Решение помечено как предложенное: обсуждается вместе с составом видов документов.

6. Следствия

6.1. Что придётся сделать до запуска

Наполнить виды документов с их атрибутами, типы значений и формы: по образцам это сотни записей на вид ИК. Работа выполняется до начала оцифровки — без видов и форм Сервис неработоспособен. Это самостоятельный объём, который стоит спланировать отдельно от разработки.

Атрибуты, совпадающие у издания и рукописи, заводятся дважды: словаря, из которого их можно было бы взять готовыми, больше нет. По образцам совпадений немного — шифр, дата, ФИО составителя, — но при заведении новых видов объём стоит держать в уме (OQ-A6-14).

6.2. Что становится дешевле

Новая редакция бланка — новая форма из тех же атрибутов вида: ни правки кода, ни миграции, ни версионирования. Новый вид документа — новая запись дерева видов. Экраны проверки и выгрузка пишутся один раз на все виды карт.

6.3. За чем следить

Рассинхронизация ядра и значений. Ядро — денормализованная проекция; при правке значения его надо пересчитывать. Ошибка здесь тихая: реестр показывает одно, карта содержит другое.

Путаница двух деревьев. Дерево документов описывает данные, дерево разделов — бумагу. Понятие, положенное не в то дерево, обнаружится поздно и будет стоить дорого.

Соглашение о списках временное. Правило «значение из единственного поля бланка добавляется в список» принято до проработки распознавания и подлежит пересмотру.

Правка типа значения задевает всех, кто им пользуется. Это и есть смысл общего типа, но интерфейс ведения обязан отвечать на вопрос «где используется» — иначе общность возвращает ту же тихую поломку, от которой ушли, только этажом выше.

Лишний уровень косвенности. Путь от документа до значения проходит через две сущности вместо одной, и это удваивает число записей при хранении. Плата за то, чтобы факту заполнения было где жить.

6.4. Что решение не закрывает

Отнесение аллигат — к карте или к экземпляру фонда — остаётся открытым (см. ниже).

7. Вопросы, закрытые решением

Вопрос Ответ
Различать ли редакции бланка одного вида ИК Различать нечего: версий нет, другая редакция — просто другая форма документа
Нужен ли отдельный тип значения под отметку в клетке Да, добавлен вид bool; выражать отметку выбором из набора в одно значение было бы менее наглядно
Кто ведёт виды документов, типы значений и формы Администратор Сервиса; на первом этапе фактически разработчик
Как не дать правке ради одного вида сломать другой Атрибут принадлежит виду; общим остаётся только тип значения, и он выбирается осознанно
Чем обеспечена сравнимость карт разных видов без общего словаря Ядром карты: шифр, инвентарный номер, заглавие, автор и год сводятся в типизированные колонки
Где хранятся значения справочных наборов — тиснение, материалы покрытия, степень повреждения В ограничениях типа значения, парами «код — значение». Отдельное хранилище понадобится только для больших наборов: первый кандидат — язык издания, 589 значений
Нужен ли поиск по значению за пределами ядра Нет. Понадобился поиск по полю — поле переносится в ядро
Как версионируются виды документов Не версионируются. Изменение вида на созданные документы не влияет: значения опознаются кодом атрибута
Хранить ли в виде документа область поля на изображении Нет. Вид описывает состав; область относится к форме документа
Как выразить «экспертов сколько угодно» Самим вложением: вид «эксперт» входит в состав вида ИК, и число вложенных документов этого вида не ограничено. Отдельная кратность не нужна
Как называется единица фонда Экземпляр фонда. Слово «документ» занято абстрактным документом модели, и оставлять «документ фонда» рядом с «документом карты» нельзя

8. Открытые вопросы

Вопрос
OQ-A6-1 Аллигаты — часть информационной карты или часть экземпляра фонда; от этого зависит, в какое дерево они попадают
OQ-A6-11 Нужен ли код вида (DocKind) при наличии дерева видов — что он различает сверх самого вида
OQ-A6-12 Дата карты определена как дата передачи экземпляра в обработку. Проверить по бумаге: в поле «Дата» пишут её или дату заполнения карты
OQ-A6-13 Может ли форма размещать поля вложенных видов — печатать эксперта прямо на листе карты — или у каждого вида своя форма
OQ-A6-14 Нужно ли при заведении вида копировать состав атрибутов с существующего вида, чтобы не набивать сотни записей заново