Всем привет! На связи Владимир Колеснев и Владимир Беневоленский из ИТ-команды подразделения ДОМ.РФ Земли. Здесь мы уже рассказывали о том, чем мы занимаемся, но напомним: в ДОМ.РФ мы создаём систему автоматизации вовлечения в оборот неиспользуемого федерального имущества. Другими словами, мы разрабатываем продукт на Б24, в котором земельные участки (ЗУ) проходят долгий путь от появления в системе до реализации на торгах.
Сокращённо мы СУЗФ – система управления земельным фондом. Почему наша система работает на Битрикс24? Да бог его знает, так было задумано свыше. Но, как говорится, от своей судьбы не уйдешь. Проблема, с которой мы столкнулись, случилась бы в любом случае, независимо от того, использовали бы мы Б24 или нет.
СУЗФ – огромная система с кучей сущностей и процессов, в которой, естественно, всё работает как швейцарские часики (спасибо нашему тимлиду). Мы оперируем 25-30 сущностями и ~900 свойствами, но основные сущности – это лоты, земельные участки, торги, документы (кстати, документов у нас около трёхсот тысяч). Каждая сущность имеет свою логику создания и изменения. Это могут быть различные источники модификаций. Например, земельный участок может создаваться/модифицироваться внутри системы логикой трансформации (межевания) участков / обновляя какие-либо данные через выписку ЕГРН (единый государственный реестр) или обновляя гео-данные через интеграцию с сервисами наподобие DaData и пр. Причем нужно заметить, что обновления так же могут происходить по событию (EventHandler) и по интервалу времени (Cron).
В среднем за день у нас происходит около 35 тысяч модификаций элементов.
Проблема
И жили бы мы так долго и счастливо, но «бизнес» интересовался природой случайной модификации элемента. С ростом системы генерировать отделом разработки ответы с причинами изменений стало уже невозможно. Проблема возникла, когда мы конкретно перестали понимать, почему какой-либо элемент сущности создался или изменил свои параметры.
Идея
Возник вопрос: как бы нам красиво снять с себя ответственность за модификации элементов? Нам был нужен механизм: единая точка входа для создания/изменения элемента и разные данные в зависимости от источника.
Реализация
Чтобы решить эту проблему, мы разработали новый механизм сохранения источника модификации элемента в нашу систему. Этот механизм позволяет нам отслеживать и записывать все действия, которые привели к созданию или изменению элемента.
Как это работает? Каждый раз, когда происходит создание/обновление элемента, эта система логирует источник и список измененных полей. Например, если земельный участок был изменен через выписку ЕГРН, система сохранит эту информацию и привяжет ее к элементу участка. Таким образом, у нас всегда будет доступ к полной истории изменений элемента и понимание, почему он был создан или изменен, когда и кем.
У нас есть базовый абстрактный класс для добавления и изменения элемента (рис.1):
Внутри этого класса есть:
Метод getDescription(), который возвращает описание логики модификации/создания элемента
Метод getFields(), в котором подготавливаем поля для элемента, данные из этого метода будем получать в методе сохранения save() в базовом классе
Метод onBefore(), в котором мы можем валидировать переданные данные перед сохранением либо выполнять какую-то дополнительную логику, необходимую до сохранения
Метод onAfter() для дополнительной логики после сохранения элемента
Метод createOrUpdate(), который изменяет/создает элемент. В него мы можем передать ID элемента, если элемент нужно модифицировать.
Внутри метода createOrUpdate() мы сохраняем элемент через метод save() и сохраняем в логи результат сохранения элемента (рис.3). Внутри метода save() мы только вызываем метод createOrUpdate(), но уже из класса для работы с элементами инфоблоков (инфоблок – одна из сущностей БД Б24).
Таким образом выглядит структура источников для элементов (рис.5):
Классы источников мы наследуем от базового класса (рис.5):
Если в функцию createOrUpdate() мы передаём ID элемента, элемент обновляется, если не передаём – элемент создаётся.
Сохранение элемента происходит таким образом (рис.6):
Данные, которые мы передаем в конструктор должны содержать в себе максимальный набор «сырых» данных, а различная логика и преобразования должны содержаться уже внутри сорса. Таким образом вся логика изменения элемента для данного сорса содержится в одном едином классе.
Сохранение элемента может быть вызвано каким-либо событием (EventHanlder) либо запущено по истечении интервала времени.
Для таких случаев у нас есть два интерфейса для класса источника: Eventable и Periodic. От Periodic добавляется метод, в котором определяется, пора ли нам запустить сохранение элемента. От Eventable – метод, в котором в класс-регистратор обработчиков событий мы возвращаем событие сразу с его обработчиком.
Интерфейс Eventable на рис.6. и Periodic на рис.7.
На рис.8 мы регистрируем обработчики событий.
Итоги
Этот механизм позволяет нам решить несколько проблем.
Во-первых, он упрощает процесс отладки и исправления ошибок. Если у нас возникают проблемы с элементом, мы можем быстро определить источник модификации и найти причину проблемы. Во-вторых, он помогает повысить прозрачность работы системы. Мы можем предоставить пользователям доступ к информации о модификации элемента, чтобы они могли видеть, какие действия привели к изменению элемента и убедиться в его достоверности.
Кроме того, это позволяет анализировать данные и выявлять тенденции и паттерны в модификации элементов. Мы можем использовать эти данные для оптимизации процессов и повышения эффективности работы системы.
Дополнительным бонусом стало то, что теперь мы можем автоматически генерировать документацию по источникам создания и изменений элементов, основываясь на методе getDescription.
Поэтому можно смело сказать, что механизм сохранения источника модификации элемента – мощный инструмент для улучшения работы нашей системы. Он позволяет нам легко отслеживать и анализировать изменения элементов, обеспечивать прозрачность и надежность работы системы.