Rules: physical model
Правила: исполнение
Таблицы
Хранение данных в базе описано в таблицах.
-
Таблицы следует объявлять явно для каждого используемого набора классов ключей, а свойства с этим набором параметров — размещать в них опцией
TABLE. Автоматически созданной таблицы_auto_...в логике быть не должно: ее имя строится из идентификаторов классов свойства, отсортированных по алфавиту, поэтому изменение набора классов переносит данные в другую таблицу, а перестановка тех же классов местами оставляет их в той же. -
Свойства с одинаковым набором параметров, которые обычно читаются вместе, следует хранить в одной таблице: тогда их чтение не требует соединения таблиц.
-
Опцию
NODEFAULTследует использовать для таблиц узкого назначения, в которые свойства должны попадать только явно. -
Опцию
FULLследует указывать для таблицы, которая содержит все объекты классов своих ключей. Она влияет только на способ выполнения запросов, поэтому указывать ее для таблицы, которая заполнена не для всех объектов, нельзя. -
Политику именования следует выбирать на старте проекта. Короткая политика делает имена в базе читаемыми, но при большом количестве материализованных свойств требует явных имен полей, чтобы имена оставались уникальными.
Материализации
Сам механизм описан в материализациях.
-
Материализовать следует агрегированные свойства, которые читаются или используются в условиях фильтрации значительно чаще, чем меняются данные, от которых они зависят.
-
Материализовать не следует свойства, у которых значение не
NULLдля бесконечного числа наборов объектов, — такое свойство материализовать нельзя. Типичный случай — свойство с параметром встроенного класса, например датой, не ограниченным условием. -
Материализация цепочки промежуточных свойств умножает работу при изменении данных: следует материализовать результат, который действительно читается, а не каждый шаг вычисления.
-
Свойство, зависящее от часто меняющихся данных и читаемое редко, материализовать не следует — обновление хранимых значений будет выполняться при каждом изменении.
-
После изменения определения материализованного свойства или прямого исправления данных в базе хранимые значения следует пересчитать оператором
RECALCULATE.
Индексы
Сам механизм описан в индексах.
-
Индексы следует создавать для свойств, по которым выполняется фильтрация или поиск в формах и запросах, и не следует создавать «на всякий случай»: каждый индекс обновляется при каждом изменении значений его полей.
-
Индексировать можно только материализованные свойства, поэтому индекс по вычисляемому свойству требует его материализации — и решение о ней принимается по правилам материализации из этой же статьи — свойство материализуют потому, что его читают или используют в фильтрации заметно чаще, чем меняются данные, от которых оно зависит, а не ради индекса.
-
Составной индекс следует создавать, когда фильтрация идет сразу по нескольким полям одной таблицы; первыми в нём должны идти поля, ограничиваемые равенством, а за ними — поле, ограничиваемое диапазоном: именно на такой форме btree-скан сужается лучше всего.
-
Индекс, дублирующий автоматически создаваемые, создавать не следует: уникальный индекс по всем ключам таблицы и индексы по суффиксам ключей уже существуют.
-
Для полей, по которым выполняется поиск операторами
LIKEиMATCH, следует использовать одноименные типы индексов, а не обычный индекс.
Примеры
Таблица на каждый набор классов ключей, свойство размещено в неё опцией
TABLE, материализован читаемый результат — построчный sum оставлен
вычисляемым, потому что правило 3 запрещает хранить промежуточный шаг
цепочки. В составном индексе поле, ограниченное равенством, стоит перед
полем, ограниченным диапазоном, как требует правило 3 правил индексов.
TABLE order (Order);
TABLE orderDetail (OrderDetail);
TABLE skuStock (Sku, Stock);
date = DATA DATE (Order) TABLE order INDEXED;
sum (OrderDetail d) = quantity(d) * price(d);
sum (Order o) = GROUP SUM sum(OrderDetail d) BY order(d) MATERIALIZED TABLE order;
INDEX customer(Order o), date(o);
Правила: модули
-
Ассистент ОБЯЗАН разбивать код lsFusion на модули по доменной логике или функциональной области, а не по произвольной технической группировке.
-
Ассистенту СЛЕДУЕТ предпочитать относительно короткие модули.
Один широкий модуль НЕ ДОЛЖЕН продолжать расти, когда логика естественно разделяется на более мелкие связные модули.
-
Ассистент ОБЯЗАН применять низкую связанность и высокую связность: тесно связанные классы, свойства, действия и формы СЛЕДУЕТ держать вместе, а межмодульные зависимости СЛЕДУЕТ держать узкими и явными.
-
NAMESPACEмодуля СЛЕДУЕТ выбирать по общему бизнес-домену, а не по полному имени модуля. -
Когда модуль относится к существующему семейству доменов, ассистенту СЛЕДУЕТ переиспользовать пространство имён этого семейства для всех его элементов.
Новое пространство имён СЛЕДУЕТ создавать только для действительно нового домена, а не для каждого технического подмодуля.
-
Если имя модуля уже совпадает с предполагаемым пространством имён домена, опускание
NAMESPACEдопустимо, поскольку lsFusion использует имя модуля по умолчанию.Иначе ассистенту СЛЕДУЕТ указывать
NAMESPACEявно. -
Ассистенту СЛЕДУЕТ использовать
REQUIRE,EXTEND, абстрактные свойства / действия и расширения форм для связывания модулей вместо дублирования логики или создания god-модуля. -
Прежде чем добавлять код в существующий модуль, ассистент ОБЯЗАН проверить, относится ли логика к домену этого модуля.
Если нет, ассистенту СЛЕДУЕТ создать или расширить более подходящий модуль.
-
Вводя новый модуль, ассистент ОБЯЗАН осознанно выбирать зависимости и избегать циклических или ненужных зависимостей.
-
Чтобы использовать свойство, действие, класс или форму из другого модуля, этот модуль ДОЛЖЕН быть достижим из цепочки
REQUIREтекущего модуля — напрямую или транзитивно через другие подключённые модули.Если модуль-владелец не входит в транзитивное замыкание
REQUIRE, платформа выдаёт ошибку «Property not found» (или аналогичную «not found») при запуске.Ассистент ОБЯЗАН добавить модуль-владелец (или любой модуль, уже подключающий его) в список
REQUIREтекущего модуля до использования его элементов. -
Сервер поставляется со встроенными системными модулями, чьи имена НЕЛЬЗЯ переиспользовать для модулей приложения — сервер падает при запуске с ошибкой
module '<имя>' has already been added. Встроенные имена: System, Utils, UserEvents, Scheduler, Email, Time, Reflection, Security, Service, Icon, Authentication, SystemEvents, Word, WebSocket, Integration, Profiler, SQLUtils, ProcessMonitor, DefaultData, Image, Printer, Numerator, Chat, Eval, I18n, Com, Sound, Backup, OpenCV, Geo, Historizable, Schedule, Document, QZTray, Excel, Hierarchy, RabbitMQ, MasterData, Messenger, Whatsapp, Skype, Telegram, Viber, Slack.Для типовых доменных имён из этого списка (
MasterData,Document,Schedule,Numerator) ассистенту СЛЕДУЕТ добавлять к имени модуля префикс проекта.
Правила: миграция (migration.script)
-
Переименование свойства или действия, либо его перенос в другое пространство имён, меняет каноническое имя элемента. Всякий раз, переименовывая или меняя пространство имён существующего элемента, ассистент ОБЯЗАН отразить это изменение в
migration.scriptв той же правке; иначе платформа считает старое и новое имена несвязанными элементами — старый удаляется, а новый создаётся пустым. -
Для первичного (
DATA) свойства это незаметно разрушительно, и ассистент ОБЯЗАН проявлять особую осторожность. Переименование / смена пространства имён ДОЛЖНЫ быть записаны как изменениеSTORED PROPERTY(старое каноническое имя -> новое каноническое имя), что переименовывает соответствующую колонку в базе данных и сохраняет её данные. Простое изменениеPROPERTYпереносит только настройки политики безопасности и рефлексии, но НЕ сохранённые данные. -
Без записи
STORED PROPERTYпри следующем старте сервера старая колонка переименовывается в_DELETED_плюс её прежнее имя в базе — а если колонка с таким именем уже есть, просто удаляется, — а для нового имени создаётся новая пустая колонка, так что все существующие значения свойства теряются. Ассистент НЕ ДОЛЖЕН переименовывать или переноситьDATAсвойство в другое пространство имён без добавления этой записи. -
Переименование пользовательского класса или его перенос в другое пространство имён ДОЛЖНЫ быть записаны как изменение
CLASS, чтобы сохранить его объекты и их данные. Такое переименование класса может также изменить канонические имена егоDATAсвойств; они не отслеживаются автоматически и ДОЛЖНЫ быть добавлены как отдельные измененияSTORED PROPERTY.