Нередкая ситуация: бизнес хочет быстро вносить изменения в такие справочники, как продуктовый каталог и тарифы. Уровень критичности этих справочников — mission/business critical. Самый быстрый вариант — менять сразу на проде. Но, надо помнить что мы банк и если вносить изменения в такие справочники без тестирования, то в случае ошибки рискуешь получить страховки с комиссией в разы больше или меньше нужной, да ещё полный сбой оформления кредитных заявок.
Я посвятил этот пост проблеме бизнес-справочников и предложил несколько вариантов решения, проверенных на котиках на себе. Под катом вас ждут максимум конкретики и программный код С#.
<Погрузиться в изменчивый мир бизнес-справочников/>
Если нужен компромисс
Дайте возможность бизнесу вносить изменения на проде, но с условием, что значения будут меняться в рамках заранее протестированного диапазона. То есть их должен будет подтвердить системный аналитик или тестировщик, сверив со сводным протоколом тестирования. Этот протокол можно представить как таблицу: {имя параметра, допустимый (протестированный) диапазон значений}.
У такого подхода есть свои минусы:
Необходимо очень много тестировать, чтобы получить сводный протокол на все случаи. Даже количество комбинаций граничных значений всех параметров справочника будет очень большим.
Не исключён человеческий фактор. По-прежнему можно в рамках протестированного диапазона задать тариф с лишним ноликом.
Вариант для DevOps’ов
Почему бы не поступать с изменениями справочника так же, как с изменениями кода? Принимать их как условные merge request’ы и прогонять через автотестирование.
Вообще, верю, что в какой-то лучшей параллельной вселенной бизнес освоил Git и делает merge request’ы с JSON-файлами или даже со скриптами миграции БД. Но, увы, это песня не про нас…
Можно обойтись и без Git на стороне бизнеса. Достаточно просто дать бизнес-аналитикам возможность формировать патчи с изменениями справочника. А после передавать их в тестирование и внедрение так же, как это делают разработчики, когда вносят изменения в конфиги.
Выходит простая схема: открыть справочник, внести изменения, запустить их применение.
А как на практике?
Процесс изменения справочника может проходить, например, так. Бизнес-аналитик:
Вносит изменения в продуктовый каталог на одном из стендов разработки.
Меняет атрибуты элемента каталога.
Жмёт в контекстном меню каталога «Отправить патч во внедрение».
Сервис каталога, например, через GitLab API:
Создаёт в репозитории ветку biz_patch_xxx с единственным новым коммитом, содержащим изменения каталога.
Отправляет merge request.
Активирует пайплайн, который применяет патч и начинает автотесты.
Запускает стандартный процесс тестирования и публикации.
Что из себя представляет такой патч?
Я вижу три варианта: сериализованные команды CQRS (созданные при редактировании каталога), SQL-инструкции, JSON Patch.
Понятно, что с любым решением будут возникать конфликты из-за параллельных изменений. Самый простой путь с ними справиться — не принимать изменения для неактуального состояния справочника.
Если несколько пользователей начали менять справочник одновременно, то мы примем изменения от того, кто первым сохранит новые данные. Опоздавшие увидят сообщение по типу «Состояние системы изменилось, пока вы вносили изменения, пожалуйста, внесите изменения заново». Не думаю, что такое упрощение вызовет проблемы. Ситуации, когда справочники меняют много людей одновременно, относительно редки.
Реализовать такую проверку можно, если при создании патча сохранять в него хеш состояния справочника. Хеш справочника изменится, когда его отредактируют. И патчи со старым хешем приниматься не будут.
Исходный бизнес-конфиг
Предположим, что у нас есть записанный в JSON небольшой продуктовый каталог розничных кредитов:
{
"Розничные кредиты": {
"Лучший": {
"Тип": "Потребительский",
"Ставка": 0.7,
"Лучший на рынке кредитования в России": "Да"
},
"Ипотека для IT": {
"Тип": "Ипотечный",
"Гос программа": "Да",
"Ставка": 0.5
}
}
}
Меняем справочник
Дабы не усложнять статью, предположим, что бизнес-аналитик скопировал продуктовый каталог к себе на рабочую станцию, чтобы отредактировать с помощью блокнота, а лучше — VS Code с JSON-плагином, и вносит такие изменения:
{
"Розничные кредиты": {
"Лучший": {
"Тип": "Потребительский",
"Ставка": 0.6,
"Лучший на рынке кредитования в России": "Да"
},
"Ипотека для IT": {
"Тип": "Ипотечный",
"Гос программа": "Да",
"Ставка": 0.5
},
"Ипотека для военных": {
"Тип": "Ипотечный",
"Гос программа": "Да",
"Ставка": 0.45
}
}
}
Аналитик поменял ставку у одного существующего продукта и добавил ещё один новый.
Получаем патч
Пора начать внедрять наши изменения в кредитный конвейер банка. Для начала создадим патч с помощью утилиты:
static void Main(string[] args)
{
var inJson = JObject.Parse(File.ReadAllText(@"BankProducts.json"));
var inJson2 = JObject.Parse(File.ReadAllText(@"BankProductsNew.json"));
var patch = new JsonPatchDocument();
FillPatchForObject(inJson, inJson2, patch , "/");
File.WriteAllLinesAsync(@"BankProductsPatch.json", new[] { JsonConvert.SerializeObject(patch) });
}
// Взял на stackoverflow
// https://stackoverflow.com/questions/43692053/how-can-i-create-a-jsonpatchdocument-from-comparing-two-c-sharp-objects
static void FillPatchForObject(JObject orig, JObject mod, JsonPatchDocument patch, string path)
{
...
}
Утилита создаст для нас файл BankProductsPatch.json.
[
{
"value": {
"Тип": "Ипотечный",
"Гос программа": "Да",
"Ставка": 0.45
},
"path": "/Розничные кредиты/Ипотека для военных",
"op": "add"
},
{
"value": 0.6,
"path": "/Розничные кредиты/Лучший/Ставка",
"op": "replace"
}
]
Это и есть патч со всеми изменениями! Всё просто и прозрачно. Сделаны две операции: добавлен (add) новый элемент и заменён (replace) старый.
Применяем патч
Транспортируем патч в Git-репозиторий, чтобы GitLab pipeline провёл миграцию изменений. Создаём утилиту, которую будет запускать pipeline, и получаем желаемый каталог.
static void Main(string[] args)
{
var inJson = JObject.Parse(File.ReadAllText(@"BankProducts.json"));
var patchStr = File.ReadAllText(@"BankProductsPatch.json");
var patch = JsonConvert.DeserializeObject<JsonPatchDocument>(patchStr);
patch.ApplyTo(inJson);
File.WriteAllLinesAsync(@"BankProductsWithAppliedPatch.json", new[] { JsonConvert.SerializeObject(inJson) });
}
Теперь продуктовый каталог в изменённом виде (BankProductsWithAppliedPatch.json = BankProductsNew.json) существует на стороне контура разработки и тестирования. Там его можно проверить всеми возможными способами, а после отправить в ветку prod.
Решение довольно простое. И стоит заметить, что использование JSON Patch — это только один из доступных вариантов.
Заключение
Я не случайно назвал статью «Бизнес-конфиги». Изменения в некоторых бизнес-справочниках по степени влияния на бизнес-процессы в IT-системах предприятия сопоставимы с изменениями конфигов или даже кода в сервисах этих систем. Поэтому и относиться к таким справочникам нужно соответствующе.
Буду рад, если кто-то поделится в комментариях опытом и знаниями о том, как изменять, тестировать и доставлять справочные бизнес-данные в prod-контур предприятия.