Здравствуйте! Я Дина Лакеева, в разработке я с 2012 года. Сейчас я являюсь лидером системного анализа продуктового стрима в команде разработки личного кабинета МегаФона. Практически на всех своих проектах я сталкивалась с проектированием интеграций, то есть со взаимодействием различных систем или их частей. И именно эта часть проекта меня больше всего увлекала. Интеграции – это то, в чем мне всегда хотелось развиваться, и я вижу в этом бо��ьшой интерес и по сей день.
Эта статья появился на основе собственного опыта, а также консультирования коллег. Довольно часто я вижу, что поднимаются вопросы проектирования API, моделей данных, но при этом не решены концептуальные моменты, на которых строится вся логика взаимодействия и сам бизнес‑процесс. Когда я решила оформить свои мысли в статью, поняла, что хочу использовать ассоциации: система — это корабль, а проектирование новой интеграции — это отправка его в путь по морям.
Нельзя просто взять и направиться в сторону конечной точки. Сначала нужно изучить маршрут, понять, какие у корабля есть ограничения и как их учитывать, запастись спасательными кругами и обязательно подумать, как оповещать пассажиров о бедствии.
Так вот, представим, что наша система — это корабль... О чем же стоит подумать на берегу?
1. Определение ответственности вашей системы
Перед проектированием новой интеграции нужно в первую очередь понять, какая у вас система, за что она является ответственной, как хранятся данные, как они передаются. И главное - определить, какая ответственность именно на вашей системе. Иначе может произойти рассинхрон данных, деление ответственности, повтор бизнес-логики, из-за чего данные в один миг могут оказаться ошибочными, и станет непонятно, где искать правду.
Рассмотрим очень простой пример (рисунок 1).
Приложение, в котором продаются какие-то товары (назовем его «Магазин») позволяет регистрировать у себя карты пользователей. Магазин отправляет запрос на регистрацию карты пользователя в платежную систему (далее ПС), получает в ответ 200 ОК, что распознает как «Карта привязана в приложении».
Если и приложение-магазин, и ПС будут вести базу карт (в ПС полные данные, в Магазине, например, только маскированные значения, так как ИБ не позволит хранить полные данные), то процесс может оборваться, например, на этапе записи в базу данных магазина. В таком случае определить, где же все-таки истина будет сложно. Магазин будет считать, что карта зарегистрирована, отобразит ее маскированные данные в источниках средств пользователя, когда у ПС по каким-то причинам карты уже не будет или наоборот.

Если база будет только к ПС, тогда приложение-магазин будет только отправлять запросы в ПС, а все действия с базой карт будет только на ПС. В случае каких-то ошибок, будет легко определить, что произошло, и исправить.
Если все-таки ведение БД только на одной системе невозможно, например, каждый раз получать данные дорого по времени или ресурсам, стоит задуматься о синхронизации баз данных, но даже в этом случаи стоит точно знать, в какой системе данные берутся за истину.
Данный пункт касается не только понимания, где будет база данных. В первую очередь это вопрос определения границ ответственности вашей системы, выяснения, какая система является мастер-системой в бизнес-процессе или ее части. Например, если система является мастер-системой по процессу «Автокредитование» и интегрирована с системой, в которой ведутся счета по ним, будет лучше, если все расчеты и указания на списания приходили из мастер-системы. В этом случае система ведения счетов ничего бы не рассчитывала и не списывала бы сама, так как в мастер-системе может что-то резко поменяться, и система ведения счетов об этом может просто не успеть вовремя узнать.
Таким образом, четкое определение ответственной системы (а точнее понимание, какая ответственность именно на вашей системе) дает возможность делать интеграцию более простой, удобной, масштабируемой и легко дорабатываемой.
2. Какой вид межсервисного взаимодействия выбрать?
Всем известно, что бывают 2 вида межсервисного взаимодействия - синхронный и асинхронный. Здесь ��ет ничего сложного, но хочется отметить, что даже если у вас все приложение состоит из синхронного взаимодействия (или наоборот), для каждой новой интеграции стоит задумываться об этом и определять оправданность того или иного взаимодействия и с бизнесовой, и с технической точки зрения.
Стоит уточнить у системы-партнера, как процесс происходит на их стороне. Нужно ли асинхронный процесс делать на своей стороне синхронным или наоборот. Со вторым случаем на практике я не сталкивалась, но могу лишь предположить гипотетически:
У системы, с которой проектируется интеграция, процесс на своей стороне синхронный: они отвечают нам уже после завершения транзакции, планируется небольшая нагрузка и приемлемые для нас SLO. Но мы зачем-то пользуемся очередями, когда можем сделать процесс синхронным, простым и легко изменяемым.
Первый случай хочу рассмотреть чуть подробнее (рисунок 2).
В моей практике встречался кейс, когда мы опрашивали статус платежа у ПС, в то время, когда ПС принимала статусы платежей в асинхронном режиме, тем самым мы порой не успевали получить конечный статус платежа за время присутствия пользователя у экрана.

Решив, наконец, перейти на асихронный режим, мы избавились от проблем неверного информирования пользователей и потери ресурса на перезапросы (рисунок 3):

При выборе способа межсервисного взаимодействия учитывайте (если уверены в достоверности информации) способ взаимодействия по процессу на стороне системы, с которой происходит интеграция.
3. Продумайте алерты по взаимодействию с системой – партнером
Для качественной интеграции очень важно иметь возможность в реальном времени оценивать техническую доступность сервиса, а также определять процессы по управлению этой доступностью.
Одним из обеспечивающих факторов контроля и работы над доступностью является деление ошибок на типы:
LOGIC - сценарная ошибка. Когда система-партнер отвечает ошибкой, которая является одним из логических разветвлений (альтернативным сценарием);
SYSTEM_EIS - ошибка по вине внешней системы. Когда мы не можем продолжить обработку запроса из-за того, что внешняя система не ответила или ответила ошибкой, либо же ответила, но не так как мы ожидали (например HTTP 500, 502), или когда мы не можем смаппить ошибку в LOGIC, или не дожидаемся ответа по тайм-аутам;
Ошибки по нарушению SLA
Деление ошибок на данные типы нас как-то спасло, когда мы поменяли интеграцию, не меняя взаимодействие с пользователем. После релиза новой интеграции мы сразу заметили увеличение времени ответа, хотя общий фон ошибок не поменялся.
Обговорите с системой-партнером данное деление по ошибкам и SLA, выделите все ошибки с типом LOGIC, максимально отделите их от SYSTEM_EIS.
4. Подумайте об идемпотентности
Очень важный пункт при интеграции с системой - это определение, какие из методов взаимодействия неидемпотентые. На данном этапе нужно определить, какие методы могут обрабатываться повторно с одними и теми же данными, а какие – нет. Выполнение неидемпотентных действий может привести к изменениям в системе и эти изменения не могут быть повторены без гарантирования отсутствия побочных эффектов.
Особенно важно для операций типа: отправка уведомления, финансовая операция, создание сущности без идентификатора.
Нужно точно определить на чьей стороне будет обеспечиваться сам контроль идемпотентности. Конечно, лучше, чтобы контроль осуществляла мастер-система, так как в ее распоряжении логика по процессу и мастер-данные. Но бывает случаи, когда мастер-система по каким-то причинам либо не может это обеспечивать, лидо данного контроля недостаточно лишь ключа идемпотентности. Тогда можно придумать бизнес-логику на стороне другой системы. Но важно понимать, нужен ли этот контроль, как именно он будет обеспечен, продумать возможные сбои и их решения.
При каждом взаимодействии с системой думайте, нужен ли здесь контроль идемпотентности
5. Определение нагрузки и возможность кэширования данных
При каждом взаимодействии с системой нужно думать о нагрузке на систему. Определить примерную нагрузку можно по текущему взаимодействию по аналогичным точкам входа в вашу систему или, если их совсем нет, по примерным бизнесовым прогнозам. Но даже если нагрузка прогнозируется приемлемая, лучше определить возможность кэширования данных.
Нап��имер, мы получали разные источники средств (карты и подписки СБП) у пользователя из разных точек входа в приложение, думая, что они лежат в разных базах у системы партнера, и мы не делали на данную функцию кэширование. Оказалось, что мы могли при получении карт закэшировать и подписки СБП, так как они лежат в одной базе и изменяются только при совершении соответствующих операций. В итоге мы сделали кэширование и забрали данные сразу по картам и по подпискам СБП и поставили условие инвалидации на конкретные операции в нашем приложении.
Узнавайте механизм обновления получаемых вами данных у системы партнера, чтобы снизить нагрузку на ваш ресурс и на внешнюю систему
6. Не опирайтесь при проектировании новой интеграции только на документацию
Как уже было сказано в каждом пункте выше спрашивайте, общайтесь, узнавайте…
Случается такое, что мы не видим недостатков своих систем, как и наши партнеры не видят чего-то неочевидного в своих.
Не отмалчиваясь, читая только документацию, мы можем существенно улучшить процессы взаимодействия и сделать сервис, предоставляемый нами пользователям, более качественным.
