Руководство по Ревью Кода Scala/SQL (GlobalFramework)

Содержание

Руководство по Ревью Кода Scala/SQL (GlobalFramework)#

Это руководство служит рекомендацией для начинающих ревьюверов, указывая, на что необходимо обращать внимание при выполнении ревью кода. Оно содержит рекомендации, собранные разработчиками команды, и направлено на повышение качества, производительности и стабильности кода. Документ охватывает общие принципы, архитектурные и стилистические нормы, а также важные аспекты производительности и безопасности.


1. Метаописания и Структура Классов#

Именование классов#

Имя класса должно быть уникальным, на латинице в формате <Модуль>_<Имя> (единственное число, именительный падеж, например, Lbr_Book). Имена сущностей регистрозависимы и должны точно совпадать с ожиданиями фреймворка (CamelCase).

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

Именование атрибутов#

Атрибуты должны иметь осознанные имена, без транслита, соответствующие содержанию поля (camelCase).

Цель: Улучшить читаемость и документировать структуру данных, соответствовать стилю Scala и GSF.

Типы атрибутов#

Для каждого поля должно быть указано имя, тип данных и связь. Простые поля используют типы NLong, NNumber, NString, NDate и т.д.; ссылочные поля – Reference, VariableReference или GID. Проверьте, что все Reference указывают на существующий класс и тип правильный (например, GID для переменной ссылочности). Типы полей должны быть корректно указаны и соответствовать БД (например, NLong, NNumber, NBigDecimal, NString, NGid, NDate и т.п.).

Цель: Обеспечить корректную работу с null-значениями, безопасность типов, интеграцию с фреймворком и согласованность схемы БД и метаданных.

Соответствие БД и метаданных#

Названия и типы полей в метаданных должны совпадать с колонками в БД (NLongbigint, NStringvarchar/text, NDatedate). При джоинах убедитесь в совместимости типов (избегайте неявных преобразований идентификаторов и используйте корректные приведения).

Цель: Обеспечить корректную работу ORM, избежать ошибок типов и обеспечить производительность запросов.

Связи#

Проверить корректность внешних ключей/ссылок, отраженных в метаданных. Использовать GID для переменной ссылочности в одном поле. Убедиться, что все связи отражены в метаданных (поля помечены как Reference или VariableReference).

Цель: Обеспечить целостность данных, корректную навигацию по объектной модели и работоспособность связанных функций GSF.

JSON/Jsonb-поля#

Если используется динамическое расширение (JSONB), проверьте настройку контейнера (тип Json) и работающую схему ключ–значение. Также проверьте необходимость индексов.

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

Отсутствие дубликатов#

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

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


2. Конфигурация Модулей GSF#

project.yaml#

Проверьте секции конфигурации проекта (версии Scala/Java, параметры сборки, список модулей). Например: scalaFeatureRelease, sbtPlugin, modules с описанием каждого модуля – имя, source (репозиторий), branch, флаг isPublish. Убедитесь, что указаны все нужные модули (например, calculation, source, target, custom при наличии) и их настройки верны.

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

Структура каталогов#

Поддерживайте корректную структуру каталогов (как сгенерировано Configurator), включая папки src, resources, тесты и пр.

Цель: Облегчить навигацию по коду, упростить настройку IDE и сборочных скриптов, повысить согласованность между проектами.

Зависимости#

Зависимости между модулями должны быть описаны явно в build.sbt, а так же продублированы в module-info.xml как метаданные

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

Конфигурации сборки/публикации#

Конфигурации сборки (например, sbt) и плагины должны быть корректны. Названия модулей и подпроектов должны соответствовать соглашениям.

Цель: Обеспечить стабильность и воспроизводимость процесса сборки и публикации, улучшить читаемость и поддержку проекта.


3. Использование стандартных компонентов GSF#

Использование сервисов GSF#

По возможности используйте готовые сервисы фреймворка: аудит изменений, автонумерацию, группировку, поиск по шаблону, прикреплённые файлы и т.д. Например, если требуется фильтрация классов, включите «Универсальный фильтр» – он позволит фильтровать по полям класса и его коллекций на уровне БД. Группировка объектов должна работать через механизмы группировки GSF.

Цель: Снизить вероятность ошибок, ускорить разработку, обеспечить единообразие функциональности, упростить поддержку и обновление.

Кэширование и запросы#

Если используется кэширование GSF (Shared-cache), убедитесь, что для часто запрашиваемых сущностей настроены cache-index в ORM и при запросах используется .unique(). Используйте объектные запросы (OQuery) с tryCacheQueryResults() там, где это целесообразно. Проверьте стратегии инвалидирования кэша.

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

Логирование#

Для записи логов в базу задействуйте встроенный LogTransaction (выделяет отдельную лог-сессию). При большом количестве записей выполняйте commitByInterval() (например, каждые 1000 записей). Такой подход изолирует логи от основной транзакции и снижает нагрузку на БД.

Цель: Изолировать логи от основной транзакции, избежать потери логов при откате, снизить вероятность конфликтов и нагрузку на БД.


4. Обработка Ошибок и Зависимости#

Прикладные исключения#

В коде обрабатывайте только «прикладные» исключения — наследники AppException. Системные исключения не перехватывайте, а выбрасывайте дальше, пропускайте (так фреймворк сможет корректно прервать сессию).

Цель: Чётко разделить бизнес-логику ошибок и системные сбои, предотвратить скрытие серьёзных проблем и нарушение целостности данных.

Объявление исключений#

Создавайте свои исключения как подклассы AppException и заводите для них фабрику (например, object ExceptionName extends ExceptionFactory(new ExceptionName(_))). Выбрасывайте исключения через throw AppException(…) или e.raise(…) (сохранит стек вызовов). При необходимости логирования ошибки используйте отдельную лог-транзакцию.

Цель: Обеспечить единообразие, упростить обработку, легко идентифицировать тип ошибки, гарантировать запись лога при откате основной транзакции.

Логирование ошибок#

При необходимости логирования ошибки используйте отдельную лог-транзакцию. В try/catch используйте минимальное информативное логирование; причина исключения не скрывается.

Цель: Изолировать запись лога от основного процесса, избежать засорения логов, сохранить ключевую информацию для диагностики, сохранить причину исключения.

Транзакции и зависимые задачи#

Избегайте слишком длинных транзакций (несколько минут и более) – PostgreSQL плохо их переносит. Разбивайте большие операции на логические блоки и выполняйте session.commitWorkAuto() после каждой партии изменений. Например, при массовой загрузке в цикле вызывайте commitWorkAuto() через заданный интервал, чтобы регулярно сбрасывать пакет в БД. При этом используйте flush() / commit() осмотрительно: не делайте неожиданных коммитов внутри server-side методов, чтобы не прерывать ожидания отката.

Цель: Снизить вероятность конфликтов, рост WAL, улучшить общую производительность БД, уменьшить вероятность сбоев, облегчить откат, сохранить согласованность данных.


5. Особенности SQL в GSF (Валидации и Ограничения)#

Оптимизация запросов#

Всегда проверяйте план выполнения (EXPLAIN) для поиска «узких мест». Сложные запросы по возможности разбивайте на подзапросы, упрощая логику.

Цель: Выявить проблемы с производительностью, улучшить читаемость, потенциально повысить производительность.

Индексы и параметры#

При фильтрации используйте индексируемые поля (например, не фильтруйтесь по служебным столбцам типа gid без индекса). Всегда передавайте значения в запрос через параметры (? или именованные), чтобы избежать SQL-инъекций и позволить СУБД кэшировать планы.

Цель: Обеспечить быстрое выполнение запросов, защитить от SQL-инъекций, ускорить выполнение за счёт переиспользования планов.

Соединения и типы#

При JOIN таблиц по ключам используйте одинаковые типы полей (например, связывайте bigint с NLong); избегайте неявных преобразований (например, конвертации varchar и text). При необходимости приводите типы явно.

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

Рекурсивные запросы#

Если пишете рекурсивные CTE, обязательно добавьте условие выхода (обычно это WHERE id NOT IN (…) с ограничением глубины) и защиту от зацикливания. И используйте NOT EXISTS вместо NOT IN, чтобы учесть NULL и не терять результаты.

Цель: Предотвратить бесконечные циклы в запросе, обеспечить надёжность при наличии NULL-значений.


6. Требования к Структуре Scala-Кода#

Соглашения по стилю#

Соблюдайте стандарты именования Scala (классы – CamelCase, методы/переменные – camelCase) по руководству Scala. Global3-FrameWork регистрозависим, поэтому имена сущностей (классов, полей, методов) должны точно совпадать с тем, как они объявлены. Не используйте подчёркивания в именах (за исключением разделителя модуля и имени класса).

Цель: Обеспечить корректную работу фреймворка, повысить читаемость кода, облегчить его понимание и поддержку.

D/A-паттерн кода#

При генерации классов учтите, что фреймворк генерирует две иерархии: Domain- и Application-части. Доменные классы (ClassDpi, ClassDvi) перезаписываются при генерации и содержат «скелет» логики, а прикладные классы (ClassApi, ClassAvi) – расширяют их и используются для ручной бизнес-логики. Логика должна писаться только в прикладных классах (Api/Avi), доменные файлы не редактируйте.

Цель: Обеспечить стабильность при регенерации кода, изолировать бизнес-логику от автоматически генерируемого кода.

Null-типы GSF#

Для работы со значениями из БД используйте расширенные типы GSF (NLong, NNumber, NGid, NDate, NString, NBigDecimal и т.д.), которые безопасно обрабатывают null. Например, идентификаторы – NLong (фабрика nl), числовые поля – NNumber или NBigDecimal (фабрика nr). Важное правило: для финансовых расчётов используйте NBigDecimal (BigDecimal), чтобы избежать ошибок двоичной арифметики. При сравнении nullable-значений в скриптах GSF применяйте оператор === (предотвращает NPE) вместо стандартного ==.

Цель: Защитить от NullPointerException, обеспечить корректную обработку null, гарантировать точность вычислений для финансовых данных, обеспечить безопасность при сравнениях.


7. Общие Принципы и Стиль Кода#

Комментарии#

Код должен сопровождаться понятными комментариями.

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

Именование#

Переменным и атрибутам необходимо давать осознанные имена, транслит необходимо избегать. Следуйте scala-конвенциям наименования сущностей.

Цель: Улучшить читаемость, документировать структуру данных, соответствовать стилю Scala и GSF.

Форматирование#

sql и scala код должны быть отформатированы и не превращаться в «кашу».

Цель: Улучшить читаемость, облегчить понимание и сопровождение кода.

Scaladoc#

Писать scaladoc к методам, классам и переменным.

Цель: Предоставить официальную документацию к API, облегчить поддержку и использование кода другими разработчиками.


8. Безопасность и Надёжность#

null#

Использование null должно быть полностью исключено для всех ссылочных типов.

Цель: Предотвратить NullPointerException, использовать безопасные GSF-типы.

null для AnyVal#

Для значимых типов (AnyVal), таких как JObject, запрещено использовать null или его приведение (null.asInstanceOf[JObject]). Вместо этого необходимо использовать пустые экземпляры (например, JObject()), а проверку на наличие данных выполнять через .nonEmpty.

Цель: Предотвратить NullPointerException при работе с такими типами.

head / get#

Необходимо избегать использования head или get; вместо них следует использовать headOption или getOption, чтобы избежать NPE.

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

if-else / pattern-matching#

При ветвлении кода if-else необходимо указывать ветку else; для pattern-matching обязателен случай case _ =>.

Цель: Гарантировать обработку всех возможных сценариев, предотвратить MatchError, сделать код более надёжным.


9. Производительность#

.view#

При использовании нескольких комбинаторов подряд (например, arr.map.flatMap.filter) необходимо добавлять .view в начало, чтобы не создавались временные коллекции.

Цель: Улучшить производительность за счёт снижения расхода памяти.

Pattern matching в комбинаторах#

При использовании комбинаторов по карте необходимо применять pattern matching для деструктуризации кортежей. Плохо: any_map.foreach { tpe => tpe._1 + tpe._2 }. Хорошо: any_map.foreach { case (key, value) => key + value }.

Цель: Сделать код более выразительным, типобезопасным и читаемым.

Изменяемые коллекции#

В параметрах методов и возвращаемых значениях не должны использоваться изменяемые коллекции collection.mutable.

Цель: Повысить безопасность и предсказуемость кода, уменьшить вероятность ошибок.

ArrayBuffer#

Необходимо использовать ArrayBuffer вместо ListBuffer.

Цель: Обеспечить лучшую производительность для операций вставки и доступа по индексу.

Выбор коллекции#

При выборе коллекции (List, Array, Vector, ArrayBuffer) необходимо учитывать асимптотическую сложность операций (вставка, доступ по индексу), чтобы обеспечить оптимальную производительность.

Цель: Использовать подходящие структуры данных для ускорения выполнения операций.

Большие реестры#

Получение данных из больших реестров должно выполняться реляционными запросами в БД по максимальному количеству условий с предварительным session.flush(), чтобы не перегружать ОЗУ сервера.

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


10. Работа с API и ROP#

HTTP-пакеты#

Прямое обращение к http пакетам запрещено; необходимо использовать btk_httppkg.

Цель: Обеспечить централизованное управление HTTP-вызовами, соблюдение стандартов безопасности и логирования.

Файлы#

При работе с файлами необходимо использовать стандартное api — btk_fileapi.

Цель: Гарантировать корректную обработку файлов, безопасность и совместимость с инфраструктурой.

copyAro#

Метод copyAro не должен использоваться, так как все данные провайдера строки копируются в оперативную память.

Цель: Предотвратить чрезмерный расход памяти, особенно при работе с большими объектами или в циклах.

Загрузка ROP по gid#

При загрузке ропов по gid необходимо использовать Btk_Pkg.loadByGid (обёрнутый в TryApp). Полученный rop должен матчиться с нужным API, например: case Btk_ClassApi(rop) =>.

Цель: Обеспечить безопасную загрузку объектов и правильную их типизацию.

Поиск по коду#

Для поиска по коду необходимо использовать методы ____Api().findByMnemoCode(""), а не прямые запросы. Объекты API должны быть объявлены через lazy val.

Цель: Использовать встроенные механизмы GSF, повысить читаемость и поддерживаемость кода.

getVar#

Вместо конструкции api().load(getVar("idAnyClass").asNLong) необходимо использовать метод get, так как нет гарантии, что getVar вернёт значение вместо null.

Цель: Обеспечить более безопасное и надёжное получение данных.

Использование source-generated конструкций#

Для доступа к атрибутам необходимо использовать конструкции, сгенерированные генератором источников (например, rop.get(_.attr)), а не методы getByAttrName и getAttrByName, так как последние используют рефлексию, ошибки которой могут быть обнаружены только во время выполнения.

Цель: Обеспечить безопасность на этапе компиляции, предотвратить ошибки, связанные с рефлексией.


11. Работа с Базой Данных и SQL (Дополнения)#

checkworkability#

Обращение к базе данных в checkworkability должно быть исключено; нужные данные необходимо выносить в onRefresh и делать доступными через getVar.

Цель: Критично для производительности UI, предотвратить неотзывчивость интерфейса.

Вложенные циклы#

Запросы внутри вложенных циклов использовать нельзя.

Цель: Избежать экспоненциального роста числа запросов к БД, сохранить производительность.

onRefresh#

В onRefresh необходимо использовать refreshByKey(parent) вместо byKey(parent).

Цель: Обновить данные в кэше, повысить эффективность.

@FlushBefore#

При необходимости использования onRefresh на основе selectStatement в детализации документа необходимо отключать @FlushBefore, чтобы избежать ошибок при создании документа с обязательными атрибутами.

Цель: Предотвратить конфликты сессии и ошибки при инициализации документа.

OQuery и итерация#

При прогрузке в кеш через OQuery необходимо использовать итерацию по списку или получение элемента в конце, что гарантирует выполнение запроса.

Цель: Обеспечить корректное поведение ленивых запросов.

OQuery и условия#

Условия в OQuery, зависящие от полей, которые могут измениться в кеше, должны обрабатываться одним из способов: предварительный session.flush(); вынос условия в .filter результата; использование транзакционного индекса.

Цель: Гарантировать, что условия в запросе будут учитывать актуальное состояние данных.

Интерполяция строк#

Вместо интерполяции строк необходимо использовать метод on в sql запросах.

Цель: Обеспечить безопасную параметризацию, защитить от SQL-инъекций.

Symbol в on#

При передаче параметров в запросы на основе Anorm через метод on необходимо использовать Symbol("idGds"), а не символьные литералы через апостроф ('idGds), так как последний устарел.

Цель: Соответствовать современным стандартам, обеспечить корректную работу.


12. Архитектура и Дизайн#

lazy#

При объявлении переменных в классе необходимо использовать ключевое слово lazy, чтобы они инициализировались только при первом использовании.

Цель: Улучшить производительность, избежать ненужных вычислений.

dataInstall#

При регистрации типов, закладок, атрибутов, функциональных настроек, процедур в dataInstall необходимо добавлять lazy val с мнемокодами и id для зарегистрированных значений.

Цель: Улучшить читаемость и поддержку кода установки данных.

Изменение параметров#

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

Цель: Критично для стабильности API, предотвратить ошибки в использующем коде.

Новые параметры#

Перед добавлением новых параметров в метод необходимо учитывать обратную совместимость. Новые необязательные параметры необходимо добавлять в конец сигнатуры метода.

Цель: Избежать необходимости изменять весь код, который вызывает этот метод.

Сложная логика в AVI#

Сложную логику не нужно расписывать в операции Avi, если она может быть вынесена в API/PKG. AVI-операция должна сводиться к вызову метода из API/PKG, это упрощает поддержку и минимизирует дублирование кода.

Цель: Улучшить поддерживаемость кода, разделить ответственности, тестировать бизнес-логику отдельно от UI-логики.

Scala-классы с компаньоном#

При создании структур Scala-классов с объектом-компаньоном необходимо использовать решение, позволяющее проектное переопределение: def list(): List = { new List {...} }.

Цель: Обеспечить более гибкую настройку поведения классов на уровне проекта.


13. Работа с Атрибутами и Полями (Дополнения)#

Source-generated доступ#

Для доступа к атрибутам необходимо использовать конструкции, сгенерированные генератором источников (например, rop.get(_.attr)), а не методы getByAttrName и getAttrByName, так как последние используют рефлексию, ошибки которой могут быть обнаружены только во время выполнения.

Цель: Обеспечить безопасность на этапе компиляции, предотвратить ошибки, связанные с рефлексией.

getSelfVar с A.idPerson.name#

В выборках при обращении к стандартным атрибутам необходимо использовать getSelfVar(A.idPerson.name) или просто A.idPerson.asNLong вместо getSelfVar("idPerson"), это позволяет поймать ошибки во время компиляции при удалении атрибута.

Цель: Обеспечить безопасность типов на этапе компиляции.

Хранимые поля в AVI#

Хранимые поля класса в AVI должны получаться через объект А, сгенерированный генератором источников, чтобы избежать ошибок времени выполнения при переименовании атрибута в классе.

Цель: Сделать код устойчивым к переименованиям атрибутов.

var в case class в onRefresh#

В case class’ах, которые возвращаются в onRefresh, атрибуты должны быть мутабельными (var).

Цель: Обеспечить корректную работу с UI и обновление данных.


14. Строки и JSON#

Интерполяция строк#

При сборке строк из статических и динамических элементов необходимо использовать интерполяцию s"text${value}otherText" или многострочный вариант: s"""1-st line |2-nd line |.... |last line""".stripMargin. Интерполяция должна использоваться вместо конкатенации, так как она оптимальнее и более читаема.

Цель: Повысить читаемость, обеспечить оптимизацию компилятором.

.stripMargin#

Для многострочных строк необходимо использовать | и .stripMargin, чтобы сохранить форматирование и структуру файла.

Цель: Улучшить читаемость многострочных строк в коде.

StringBuilder#

При сборке очень больших строк необходимо использовать StringBuilder вместо pos.map(s"...").mkString().

Цель: Обеспечить более эффективную работу с большими строками.

scala.util.parsing.json#

Использовать нельзя, предпочтительнее JObject.

Цель: Использовать интегрированный с GSF и более безопасный тип.

Btk_JsonPkg#

Использование Btk_JsonPkg необходимо избегать из-за зафиксированного разного поведения при его использовании с JObject.

Цель: Предотвратить неожиданное поведение и ошибки.


15. Специфика UI: AVI и Списочные Отображения#

getVar / getSelfVar / A в checkWorkAbility#

В Avi.checkWorkAbility и других AVI-операциях списочных отображений значения полей необходимо получать через getVar, getSelfVar или A, а не через thisRop(), чтобы избежать ошибки load id = null в пустом списке.

Цель: Предотвратить ошибки при работе с пустыми списками.

Поля с большим объёмом данных#

В списочные отображения не должны выводиться поля с большим объёмом данных, чтобы не перегружать сервер. Например, поле sResponse журнала сообщений должно быть вынесено на закладку детализации или храниться в файле.

Цель: Повысить производительность UI и улучшить пользовательский опыт.

@FlushBefore в LookUp#

В LookUp-отображениях для onRefresh необходимо добавлять аннотацию @FlushBefore(mode = FlushBeforeMode.Disabled).

Цель: Предотвратить нежелательные сбросы сессии при обновлении данных в LookUp.

DefaultRep#deactivateOper#

Вместо oper.isActive = someCondition необходимо использовать DefaultRep#deactivateOper для деактивации операций при невыполнении условий, это обеспечивает корректную работу при различных перекрытиях.

Цель: Использовать встроенный механизм фреймворка для корректной деактивации операций.

prepareSelectStatement#

Сортировка по умолчанию и передача макросов фильтрации должны быть указаны в prepareSelectStatement, а не в selectStatement.

Цель: Улучшить структуру и читаемость кода настройки выборки.


16. Специфика UI: Кэширование, Логика и Настройки#

Переходы состояний#

Переходы состояний должны определяться сравнением nOrder и формированием условия из двух частей (из какого в какое), например: nvStateFrom < 100.nn && nvStateTo >= 100.nn, это обеспечивает работоспособность логики даже при удалении перехода.

Цель: Сделать логику переходов устойчивой к изменениям структуры состояний.

@Setter(refreshAfter = true)#

Аннотация @Setter(refreshAfter = true) должна выполнять selection.refreshItem() в конце сеттера.

Цель: Обеспечить обновление UI после изменения значения.

Кастомные выборки#

Редактируемые кастомные выборки должны быть реализованы объектным запросом, чтобы собрать пул изменений и отправить их в БД одной транзакцией.

Цель: Повысить эффективность, обеспечить целостность данных.

onRefreshExt и id#

При реализации кастомных выборок на объектном запросе результат выборки должен содержать поле id для работы onRefreshExt.

Цель: Обеспечить корректную работу механизма обновления расширенных данных.

AdditionalInfo#

Использование AdditionalInfo необходимо дважды обдумывать. Предпочтительнее получать данные через onRefreshExt.

Цель: Улучшить структуру и производительность, избегать избыточного кода.

AdditionalInfo2#

При переопределении отображения, в котором уже используется AdditionalInfo, новые вычисляемые атрибуты не должны добавляться в новый AdditionalInfo2. В OnRefresh необходимо использовать несколько case-классов: thisApi().byParent(getIdMaster).map { rop => (rop, getAdditionalInfo(rop), getAdditionalInfo2(rop)) }.

Цель: Улучшить структуру и читаемость кода обновления данных.


17. Отражение (Reflection)#

Обоснование#

Использование reflection должно быть оправдано; в общем случае оно запрещено.

Цель: Повысить безопасность типов, производительность, упростить сопровождение кода.

scala.reflect.io._#

Использовать нельзя, предпочтительнее Java-классы.

Цель: Обеспечить лучшую стабильность и совместимость.


18. Специфика Модулей и Проектов#

Модуль STK#

Для получения остатков необходимо использовать ru.bitec.app.stk.Stk_Pkg#getRemainsMulti. Для получения цены необходимо использовать ru.bitec.app.stk.Stk_Pkg#getnPrCostConsSum.

Цель: Использовать стандартные, оптимизированные методы для получения данных из модуля STK.

Модуль ACT#

Запрос оборотов должен выполняться с учётом логовой таблицы изменений через union all, чтобы получить полную и актуальную информацию.

Цель: Гарантировать корректность и полноту данных об оборотах.

Проектные модули#

При открытии MR необходимо указывать соответствующую ветку с учётом проекта, на котором разрабатывается. Например, для проекта СНГ разработка ведётся на sng-internal-dev → ветка проектного модуля gs; pdevdev.

Цель: Обеспечить правильную интеграцию изменений в проектную ветку.


19. Безопасность и Целостность Данных#

SQL-инъекции#

Все входные данные параметризованы, нет риска SQL-инъекций.

Цель: Защитить от SQL-инъекций, ускорить выполнение за счёт переиспользования планов.

Ограничения целостности#

Ограничения целостности (foreign key, unique) соответствуют метаданным.

Цель: Обеспечить корректность и согласованность данных на уровне БД.

Валидации#

Валидации на уровне приложения дублируют, но не заменяют БД-ограничения.

Цель: Обеспечить дополнительный уровень защиты и пользовательскую обратную связь.

Чувствительные данные#

Чувствительные данные маскируются в логах и не попадают в открытые сообщения об ошибках.

Цель: Защитить конфиденциальную информацию от утечки.


20. Соответствие Best Practices#

Документация#

Убедитесь, что код и конфигурации соответствуют рекомендациям из официального руководства (например, настройка логирования, работа с транзакциями, параллельные вычисления и т.д.). Всегда проверяйте разделы «Практика разработки» в документации GlobalFramework и сверяйтесь с примерами.

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

Чистота кода#

Код должен быть читабельным и понятным (часто читают больше, чем пишут). Держите методы короткими, избегайте дублирования. Имена должны ясно отражать назначение сущности. Следуйте общим принципам GSF: не используйте служебные символы (например, «!», «?», «@») в идентификаторах, оформляйте документацию и комментарии по необходимости.

Цель: Улучшить читаемость, облегчить понимание и сопровождение кода.


21. Критические ошибки#

DML через ASQL#

DML (INSERT/UPDATE/DELETE) выполняется через ASQL. — Блокирующее

Цель: Предотвратить нарушение целостности данных из-за автокоммита ASQL.

Долгая транзакция#

Долгая транзакция, захватывающая большое число строк без разбивки. — Блокирующее

Цель: Избежать проблем с производительностью и блокировками БД.

Функции GSF в массовых выборках#

Массовое использование getattribute/getmnemocode/getheadline в больших выборках/отчётах. — Блокирующее

Цель: Избежать чрезмерной нагрузки на БД.

Арифметика с N-типами#

Арифметика с N-типами без проверки nullable (.isNotNull). — Блокирующее

Цель: Предотвратить NullPointerException и ошибки времени выполнения.

Конкатенация строк SQL#

SQL-строки формируются конкатенацией с пользовательскими данными. — Блокирующее

Цель: Предотвратить уязвимость к SQL-инъекциям.

Дублирование логики#

Необъяснимое и несогласованное дублирование логики в разных модулях. — Блокирующее

Цель: Упростить сопровождение, снизить вероятность ошибок, упростить внесение изменений.