Я работаю архитектором. Последние лет 5 я довольно много работаю с разными enterprise компаниями и я довольно интенсивно вовлечен в процесс дизайна архитектуры.Хочу написать об устойчивом паттерне повторяющемся из компании в компанию.

Как-то так устроена архтектура Travelport
Как-то так устроена архтектура Travelport

Это не продающая статья и не блог компании, постараюсь кратко и по делу, но с примерами

Есть условно три уровня зрелости при дизайне нового или редизайне существующего механизма. Особенно при редизайне.

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

  2. Посмотреть как оно сделано сейчас, если это редизайн или как оно сделано в похожем месте, если это что-то новое и предложить похожее только на новый лад.

  3. Постараться менять как можно меньше кода и вкрячить куда-то в глубь слоя библиотек какой-то трюк который временно решит эту конкретную проблему.

Примеры и немного деталей

Тип 1 vs тип 2, детали несколько изменены

Есть single tenant система которая показывает алармы от сетевого оборудования. С алармом надо показывать некую дополнительную информацию о том что этот тип аларма значит. Сама информация меняется медленно, может быть раз в месяц. Читается из отдельного репозитория.

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

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

Тип 1 в данном случае выглядит так. А давайте сделаем нано сервис который эту инфу будет возвращать прям из S3 напрямую вбраузер. Можно было бы вообще без сервиса — прям в интернет пачку json выложить, но нам нужна авторизация так что сделаем через нано сервис.

Тип 1 vs тип 3, детали тоже изменены, немного.

Делается NDC конектор для одной из GDS. Чтобы не погружаться в мир тревела — есть большая компания через которую продается 10+% всех билетов по земному шару и у нее есть коннектор к БД авиакомпании с информацией о доступных билетах.

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

Все вроде как сделано, конектор работает, но есть тонкость. Надо маскировать данные кредитных карт и в логах, а по требованиям в лог пишется полный текст всех запросов и ответов. А поскольку стандарт несколько плавает, то одного понятного места где эти данные сидят нет. И одного формата нет. Где‑то номер кредитки с пробелами, где‑то без и т. д.

В целом задача решается по варианту тип 2 — накручивается regexp парсеры вокруг логирования которые вырезают оттуда все что похоже на данные кредитки. Но есть проблема — в запросе для одной из компаний данные кредитки так хитро лежат что простой regexp не справляется.

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

Код сервиса → GDSFramework.jar → тут идет 3–4 промежуточные библиотеки → RESTClient.jar → GDSLogging.jar

И вот в последней библиотеке сидит код который собственно в лог то и пишет. Чтобы жизнь не казалась медом GDSFramework.jar используется одновременно примерно 100+ другими микро сервисами, может и 500+, точно не скажу, но 100 точно.

Что предлагает архитектор одной из команд.

А давайте мы в библиотеке GDSLogging.jar заведем ThreadLocal переменную, в нее будем класть код авиакомпании и если там написано скажем LHR то мы вот такую дополнительную логику накрутим. Решение даже в общем работает (я думаю этот код и сейчас в проде крутится).

Но есть нюансы

  1. Код ломкий и если меняется формат дополнительного инфо поля, а компания может поменять его просто так никому не говоря, это поле читается человеком, то все маскирование ломается.

  2. Если надо сделать такой же трюк для другой компании, то придется менять код системой библиотеки. Это плохо по двум причинам. А) надо обновить 100+ микро сервисов которые эту библиотеку используют а это 20+ разных команд. Б) когда количество авиакомпаний для которых требуется исключение будет расти у нас системная библиотека отвечающая за логирование будет пухнуть кодом специфичным для авиакомпаний.

Короче ломко и адски не очевидно. Плюс обновление библиотеки в глубина иерархии для всех сервисов, всем командами занимает где-то 6+ месяцев.

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

Выводом особо не будет но если прям коротко то я при дизайне стараюсь понять что мы делаем

  1. Дизайним новое решение вот ровно под задачу

  2. Копируем существующее, что не всегда плохо, но важно понимать что именно происходит

  3. Вообще никого решения не приносим, а тупо добавляем if .. then костыликов в случайные места.

Комментарии (3)


  1. Dhwtj
    12.02.2025 12:42

    Я бы сделал так:

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


  1. SkillMax999
    12.02.2025 12:42

    Занимательная статья) спасибо автору.


  1. andrewzaitsev
    12.02.2025 12:42

    Если статья начинается с «я работал в enterprise компаниях» может дальше не стоит читать?