Я работаю архитектором. Последние лет 5 я довольно много работаю с разными enterprise компаниями и я довольно интенсивно вовлечен в процесс дизайна архитектуры.Хочу написать об устойчивом паттерне повторяющемся из компании в компанию.
Это не продающая статья и не блог компании, постараюсь кратко и по делу, но с примерами
Есть условно три уровня зрелости при дизайне нового или редизайне существующего механизма. Особенно при редизайне.
-
Посмотреть на задачу, понять функциональные или не функциональные требования и предложить самое простое из возможных и самое не ломкое решение. Ломкость это вообще бич любого энтерпрайза.
-
Посмотреть как оно сделано сейчас, если это редизайн или как оно сделано в похожем месте, если это что-то новое и предложить похожее только на новый лад.
-
Постараться менять как можно меньше кода и вкрячить куда-то в глубь слоя библиотек какой-то трюк который временно решит эту конкретную проблему.
Примеры и немного деталей
Тип 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 то мы вот такую дополнительную логику накрутим. Решение даже в общем работает (я думаю этот код и сейчас в проде крутится).
Но есть нюансы
-
Код ломкий и если меняется формат дополнительного инфо поля, а компания может поменять его просто так никому не говоря, это поле читается человеком, то все маскирование ломается.
-
Если надо сделать такой же трюк для другой компании, то придется менять код системой библиотеки. Это плохо по двум причинам. А) надо обновить 100+ микро сервисов которые эту библиотеку используют а это 20+ разных команд. Б) когда количество авиакомпаний для которых требуется исключение будет расти у нас системная библиотека отвечающая за логирование будет пухнуть кодом специфичным для авиакомпаний.
Короче ломко и адски не очевидно. Плюс обновление библиотеки в глубина иерархии для всех сервисов, всем командами занимает где-то 6+ месяцев.
Решение тип 1 может выглядеть например так: А давайте сделаем отдельное хранилище для запросов/ответов, будем все складывать туда. К хранилищу сделать специальный уровень доступа, ограничивать доступ только к отдельным типам. Сделать отдельный набор сервисов — обфускаторов и т.п.
Выводом особо не будет но если прям коротко то я при дизайне стараюсь понять что мы делаем
-
Дизайним новое решение вот ровно под задачу
-
Копируем существующее, что не всегда плохо, но важно понимать что именно происходит
-
Вообще никого решения не приносим, а тупо добавляем if .. then костыликов в случайные места.
ссылка на оригинал статьи https://habr.com/ru/articles/881778/
Добавить комментарий