Большую часть своей трудовой жизни я занимаюсь различными финтех-продуктами — Яндекс.
Деньги, 1ЦУПИС и так далее.
Последние два года я разрабатывал еще одно платежное решение и хотел бы рассказать о некоторых проблемах, с которыми мы столкнулись.
Но мне интересно поговорить не только о появившихся решениях, но и, в первую очередь, о том, как вообще можно думать об архитектуре сложных систем.
Что мы делаем
В бумажном кошельке мы обычно храним банковские карты, немного денег, проездной и дисконтные карты соседних магазинов.Цифровой кошелек позволяет хранить почти то же самое, но внутри вашего мобильного телефона.
По сути, цифровой кошелек дает пользователю возможность использовать все богатство инструментов финансовой системы: от банков до магазинов.
Компания OpenWay Group уже давно является лидером рынка по созданию платежных (карточных и других) решений, и именно в ее рамках мы с командой начали создавать кошелек как продукт. Понятно, что у такого продукта могут быть самые разные покупатели:
- Государственные платежные системы и крупные банки, с сотнями миллионов пользователей, тысячами платежей в секунду, огромными бюджетами и опытной командой;
- Маленькие региональные банки мечтают о миллионе клиентов и нескольких десятках платежей в секунду и не имеют денег даже на нормального администратора базы данных.
- Клиенты из Европы, где GDPR и множество других правил, каждый шаг прописан и более-менее соответствует законам;
- Клиенты — из третьего мира, где законы пишутся на лету, иногда под конкретный кошелек или банк;
- Давно зарекомендовавшие себя компании со своей существующей инфраструктурой, несколькими дата-центрами и консервативным подходом к новым технологиям.
- Стартапы, которые еще даже не задумывались о том, какая инфраструктура им нужна.
Проект создавался как внутренний стартап, с небольшой командой, которую легко можно было накормить парой пицц, но со свободой в выборе технологий и бизнес-процессов, а также мы могли использовать всю накопленную экспертизу OpenWay.
Архитектура
Существует много разных определений архитектуры, но мне больше всего нравится цитата с сайта Мартина Фаулера: Архитектура – это важные вещи.Что бы это ни было.
(С) Ральф Джонсон
Те.архитектура — это важные вещи, какими бы они ни были.
Прекрасное определение, но не очень применимое на практике.
Далее я опишу несколько подходов к тому, что для меня архитектура и как можно посмотреть на архитектуру системы для довольно сложного проекта.
Архитектура как необходимый выбор
Прежде всего, архитектура – это выбор.Мы выбираем технологии, архитектурные стили, конкретные модели и решения.
Мы выбираем, что хотим делать, а что точно не хотим делать.
Выбор, конечно, уникален для каждого продукта, но есть несколько основных технических решений, которые необходимо принимать почти в каждом проекте.
Одним из них является выбор базы данных.
Обычно базы данных нужны для двух разных вариантов использования, и я не рекомендую их путать друг с другом: OLTP — как быстро обрабатывать и надежно хранить события, происходящие прямо сейчас; OLAP — как предоставить аналитикам всю необходимую информацию.
Это два разных мира, они требуют разных технологий.
В нашем случае в OLTP нам понадобилось:
- Надежность.
Поскольку это финтех, нас всегда спрашивают, насколько у нас все хорошо с надежностью.
- Масштабируемость, потому что бедный клиент с миллионом пользователей — это одно.
Другой — крупный банк со 100 миллионами пользователей и 5 тысячами платежей в секунду.
Надо как-то решать проблемы обоих.
- Бесплатно, потому что не все готовы платить за корпоративный Oracle со всеми его возможностями.
- Простота использования, поскольку клиенты разные и не у всех есть администратор базы данных.
- Oracle стоит дорого;
- Почему-то банки не очень любят PostgreSQL. Возможно, из требований безопасности или по старине, но это риск, который нужно учитывать.
А также то, что опыт использования PostgreSQL в банках встречается редко.
- О Монго лучше вообще не упоминать — там не все хорошо как с надежностью (она была не очень, но финтех злопамятен), так и с производительностью.
FoundationDB
Об этой базе мало кто слышал, кроме меня.Что это? Распределенная масштабируемая транзакционная (ACID) база данных с открытым исходным кодом и тщательное тестирование.
Эта бесплатная база данных с поддержкой полных транзакций была куплена Apple, но осталась открытой.
Внутри Apple на ней работает очень многое — тысячи серверов, миллионы транзакций в секунду.
Его используют и другие крупные компании, например, Snowflake. База проста в развертывании и обслуживании, достаточно быстра и, что самое главное, очень надежна.
Ведущий специалист по распределенным системам и создатель популярных тестов Jepsen на вопрос о тестировании Foundation DB ответил: «Я не тестировал тональный крем отчасти потому, что их тестирование кажется более строгим, чем мое» (с) Кайл Кингсбери (@aphyr).
Лучшей рекламы трудно придумать.
Конечно, тесты Jepsen проводила команда FoundationDB, и мы тоже тестировали.
Эта база данных действительно работает надежно.
Итак, мы выбрали решение, которое является надежным, масштабируемым и простым в обслуживании.
Но, увы, обеды не пропадают даром, а у FoundationDB масса своих проблем.
Нет платной поддержки
Вы не можете пойти к большому, богатому парню и сказать: «Возьми деньги и поддержи ее для меня».Поэтому мы сами оказываем поддержку нашим клиентам.
Нам нужно было обучить специалистов, наладить контакт с разработчиками и понять продукт настолько, чтобы участвовать в его разработке.
Низкоуровневый и довольно сложный API
Это база данных «ключ-значение», которая работает на уровне «взять байты как ключ и поместить туда еще байты как значения» — и все! Поэтому мы написали нашу библиотеку с поддержкой асинхронного доступа, типизации, сериализации в CBOR (потому что она компактнее, чем в JSON и быстрее) и поддержкой ведения домашнего хозяйства.
В общем, функцию удаления устаревших данных для баз «ключ-значение» реализовать не очень тривиально; нужно грамотно организовать ключи, но об этом, надеюсь, расскажу позже
Отсутствие ограничений доступа в многопользовательских системах
Мы добавили в библиотеку криптографию и управление ключами — и шифруем все данные, которые попадают в базу данных.Этот метод более безопасен, чем ограничение прав доступа для отдельных пользователей.
Слабые инструменты
Мы сделали собственный механизм миграции типа Flyway, но более совершенный, с поддержкой параллельных и сверхдлинных миграций, управлением зависимостями между версиями баз данных и так далее.По опыту эксплуатации мы довольны таким выбором, хотя иногда мечтаем об открытом, надежном и горизонтально масштабируемом PostgreSQL ;) При выборе базы данных для OLAP-задач все гораздо проще, так как есть Clickhouse, который решает все наши задачи.
Конечно, это не все технологические решения, которые мы сделали.
Проще перечислить другой стек: Kotlin, ktor, Flutter, React, Rest level 1. Мы так или иначе обосновали и зафиксировали каждый выбор.
Архитектура как язык
Архитектура – это не только выбор; архитектуру также можно рассматривать как язык описания системы.Таких языков в ИТ достаточно много, наиболее наглядный пример — паттерны проектирования.
Вы можете сказать любому разработчику в мире: «Я хочу здесь синглтон, а здесь стратегию», он вас поймет и все сделает. Но, к сожалению, таких языков для более высокого уровня архитектуры мало.
Луковая архитектура или Чистая архитектура дает терминологию, но она очень расплывчата, а «у нас микросервисная архитектура» вообще не означает ничего конкретного.
Поэтому архитектурный язык приходится придумывать самостоятельно — исходя из тех параметров, которые нужны для конкретного продукта.
Мы сфотографировали наши сервисы и описали, «какие задачи решают сервисы», «какие шаблоны настройки мы используем», «какие требования безопасности нужны» и в результате получили простой язык, на котором можно легко задать вопрос разработчик «создает скриптовый сервис с логикой настройки через DSL», и никаких дополнительных объяснений не требуется.
Выглядит это так (хотя для другого продукта будут совсем другие типы сервисов и другой язык описания архитектуры):
Архитектура как управление сложностью
Программирование — это управление сложностью, и архитектура тоже.Когда у вас небольшая команда и вам нужно выполнить работу довольно быстро, сложность становится вашим самым большим врагом.
Какие, на мой взгляд, самые интересные задачи в финтехе?
Бизнес-сценарии
Например, онбординг.Пришел новый пользователь и хочет зарегистрироваться в системе.
Необходимо зарегистрировать его логин и пароль в системе управления идентификацией, внести свои персональные данные в систему хранения персональных данных, ему необходимо создать учетную запись в АБС, проверить в каком-нибудь госоргане, правильный ли у него паспорт, и зарегистрироваться другая система, что он не террорист ли он.
Это примерно десять шагов, полдюжины подсистем и десятки сервисов.
Все эти шаги должны быть выполнены.
Если что-то где-то упало, нужно подумать, что с этим делать.
В современных системах оплата также представляет собой сложную логику.
У пользователя может быть кошелек одного банка, в котором находится карта другого банка, ему необходимо вывести с него деньги, оплатить необходимые комиссии, перевести остаток денег на PayPal, отправить сдачу на благотворительность и так далее.
.
Оказывается, шагов тоже десятки, несколько подсистем и десяток участвующих сервисов.
Блокировка, KYC-менеджмент, возвраты — все это требует достаточно сложных распределенных бизнес-процессов, которые нужно делать быстро (потому что их много) и максимально надежно (потому что это финтех).
Пользователь не поймет, если его деньги где-то застряли — лучше, чтобы платеж не прошел, чем неизвестность.
Как обычно решаются такие проблемы?
Саги
Слово сага, как и микросервис, перестало что-либо означать.Есть что-то более-менее нормальное определение и оригинальный статья 1986 Коротко:
- Разбиваем бизнес-процесс на множество отдельных этапов;
- Для каждого шага проектируем компенсацию, то есть обратную операцию;
- При выполнении скрипта записываем список выполненных шагов;
- В случае ошибки прогоняем все компенсации сделанных шагов от конца к началу.
Обычно не нужно отменять процесс, а завершить его другим способом.
Вернее, мне нужно, чтобы мой платеж завершился, например, если Сбербанк упал, то оплатить через Альфа-Банк или еще что-нибудь, просто чтобы завершить оплату мобильного телефона.
Компенсация не всегда возможна.
До сих пор довольно много сервисов предоставляют однофазное API, в результате компенсировать запрошенный платеж невозможно.
Компенсация зачастую нужна не за операцию, а за весь процесс.
Например, вместо компенсаций, обратных операций и так далее зачастую можно просто отправить оператору сообщение: «Что-то здесь пошло не так, разберитесь руками, пожалуйста».
Оператор будет работать лучше.
Трудно учесть ограничения всей бизнес-операции.
(время выполнения, бюджет транзакции и т. д.) Бизнес-транзакции обычно имеют довольно сложный набор ограничений: сколько времени на них можно потратить, сколько параллельных запросов можно сделать к какому-то внешнему сервису и так далее.
Соблюдать эти ограничения при осуществлении компенсации непросто.
Код оказывается довольно сложным.
Я знаю, что многим людям нравится создавать саги посредством хореографии.
На мой взгляд, это лучший способ всего за полгода создать категорически нечитаемый код, в котором потом никто не разберется.
Но даже саги с оркестровкой выглядят довольно грустно.
Вам нужно подумать, какие события вы генерируете, прописать обратные шаги и критерии срабатывания компенсации в каком-то обработчике.
Сам код операции становится расплывчатым, и никто, кроме программистов, не может его правильно прочитать.
В общем, меня давно мучает вопрос, как именно совершать сложные бизнес-операции.
Года 2-3 назад я говорил о настойчивых актерах как об отличном решении этой ситуации.
Но сейчас я предпочитаю оркестровку через рабочий процесс.
Рабочий процесс
Наиболее известной реализацией этого подхода является проект Uber Cadence. Это очень похоже на сагу:- Мы описываем процесс как императивный простой код отдельных шагов;
- При выполнении каждого шага мы сохраняем его результат где-то в персистентности;
- Если у нас возникают проблемы с шагом, мы пытаемся повторить его, доведя до конца;
- Если сервер, на котором запущен бизнес-процесс, выходит из строя, мы повторяем весь процесс с начала на другом сервере.
А поскольку шаги у нас сохранены, то добавляем их результаты, чтобы лишний раз не увеличивать нагрузку.
- Для исключительных ситуаций мы пишем один и тот же рабочий процесс, состоящий из тех же шагов.
В целом мне нравится тупой, простой, последовательный код без какой-либо сложности, потому что такой код легче тестировать и исправлять, а также легче найти разработчика для поддержки.
Вы даже можете проверить это у аналитика.
У Cadence есть версия с открытым исходным кодом temporal.io. К сожалению, нам он не очень подошел, так как очень сложен в эксплуатации.
Вот почему мы написали свой собственный велосипед.
Гайдн4к
Эта библиотека Kotlin, использующая в качестве хранилища FoundationDB, названа в честь композитора Гайдна, создателя симфонической музыки.Это библиотека, а не служба, и ее можно подключить куда угодно для выполнения бизнес-транзакций.
Разрабатывая библиотеку, мы думали об удобстве и производительности.
Удобство обеспечивает DSL на Kotlin. Мы собираем отзывы разработчиков и стараемся сделать написание бизнес-скриптов (спектаклей) максимально удобным и простым.
Производительность хорошо описана в FoundationDB. Нам нужно иметь возможность выполнять более 100 000 одновременных сценариев и до 20 000 шагов в секунду.
Это выглядит так:
Гайдн4к.
Сценарий адаптации Это очень простой сценарий адаптации: мы берем на вход определенный набор данных и начинаем игру.
Сначала мы получаем код следующего пользователя, затем создаем пользователя в рамках шага, после чего возвращаем его код обратно.
Если что-то пойдет не так (в случае ошибки), мы удаляем данные из используемых сервисов.
Кстати, отдельный шаг не обязательно должен быть идемпотентным (т.е.
при повторном применении давать тот же результат, что и первый), это достаточно просто возможность повторения операции без нежелательных последствий .
Например, взятие следующего числа из последовательности не является идемпотентной операцией, но это безопасная операция.
Код воспроизведения может быть более сложным:
Гайдн4к.
Развитие пьесы Здесь мы гарантируем, что весь playbook является одноразовым (для каждого телефона будет выполняться только один экземпляр этого playbook), указываем максимальное время выполнения, описываем ограничения на вызов внешних сервисов и ожидаем поступления сообщений (сигналов) от еще одна игровая книга.
И так далее.
Очередь
Раз у нас есть передача сообщений от одного спектакля к другому, то нам нужны очереди.Конечно, у очередей есть много других целей:
- События, отправляемые с сервера клиенту (сообщения, отправленные сервером).
Очень удобно асинхронно и оперативно оповещать клиента о происходящих событиях.
- Внутренние события (отправка СМС, отчеты по расписанию и т.п.
);
- Очереди пользовательских задач (изменение параметров кошелька и т.п.
).
Я хочу много очередей: по одной очереди на каждого пользователя, на каждый бизнес-процесс, в том числе на каждый еще не совершенный платеж.
В результате в то же время система нуждается в несколько миллионов очереди.
Однако все эти очереди довольно малы – каждая не более тысячи события.
При этом самих событий, требующих обработки, достаточно много — десятки тысяч в секунду .
Гарантии обработки событий не очень строгие; хотя бы один раз достаточно.
К сожалению, ни одно из популярных решений не может обеспечить такой набор нефункциональных требований.
ты Кафка До недавнего времени существовало жесткое ограничение на количество разделов (независимых очередей, для которых соблюдалось требование FIFO).
Сейчас его удалили, но поддерживать миллион разделов в Кафке по-прежнему очень сложно.
Легко создать множество одновременных очередей, используя PostgreSQL или другую современную СУБД (два года назад я сказал , как именно), но обеспечить 10 тысяч событий в секунду будет сложно.
Проблема, конечно, решаемая, но требует хорошего администратора базы данных и мощного сервера, а эксплуатироваться все равно будет очень сложно.
Другие решения, такие как КроликMQ или НАТС , не предоставляют требуемых гарантий или производительности.
Итак, нам пришлось сделать свой собственный велосипед:
- Сами очереди мы храним в FoundationDB. Одна или несколько соседних записей хранят несколько событий, связанных с этой очередью.
- Kafka используется для распространения информации среди всех воркеров о том, что в какой-то очереди появилось новое событие.
Конечно, нужно аккуратно относиться к ситуациям перераспределения очередей между работниками, ошибкам и так далее, но это не очень сложно.
Результат масштабируемый, достаточно надежный, а также удовлетворяет нашим требованиям к производительности.
Теоретически можно было бы обойтись и без Кафки, реализовав всё только на FoundationDB, но это было бы сложнее.
Однако, возможно, исходя из опыта эксплуатации, мы перейдём на использование только одной базы данных.
В результате от разработчика скрыты проблемы разработки надежных бизнес-транзакций в ненадежной мультисервисной среде.
Потому что архитектура — это управление сложностью.
Архитектура как точки зрения
Архитектура, помимо всего прочего, — это набор точек зрения, набор точек зрения на вашу систему.Используемые сервисы, соединения и базы данных можно рассматривать с разных точек зрения.
Для конкретного продукта одна из самых важных точек зрения: «Как нам это настроитьЭ»
Стратегии конфигурации и настройки
У каждого нашего клиента, конечно, есть свое представление о том, как должна работать платежная система и как должен работать кошелек.Поэтому нам приходится использовать множество различных стратегий, чтобы подготовить наше решение для клиента:
- Варианты конфигурации.
Например, файл с конфигурацией, в которой указано, сколько символов находится на банковском счете в данной конкретной стране.
- Баллы за добавление функциональности.
Хотелось бы, чтобы в системе были заранее предусмотрены места для добавления нового функционала, точки добавления бизнес-логики, специфичные для конкретного клиента и только для него.
- Точки расширения функциональности.
Иногда функционал общий (например, блокировка пользователя), но для каждого клиента его нужно реализовать немного по-разному.
Необходимо заранее предусмотреть баллы для разных вариантов схожей бизнес-логики.
- Настройки.
Иногда нужно просто переписать весь код — взять какой-нибудь сервис и сделать его копию под конкретного клиента.
Чем меньше умственного напряжения и когнитивных сложностей в процессе развития, тем быстрее он протекает. Для реализации гибкой настройки функционала мы активно используем DSL в Kotlin.
Котлин DSL
Kotlin — это язык, на котором удобно писать DSL, а JVM позволяет прямо во время выполнения брать код Kotlin из текстового файла, компилировать его на лету и использовать скомпилированный код прямо во время выполнения.Мы этим очень активно пользуемся, потому что это быстро, надежно, стандартизировано и эффективно.
У каждой задачи есть свой небольшой DSL, потому что ее легко выполнить.
Эти фрагменты кода хранятся в сервисе общей конфигурации, компилируются при запуске и выполняются при необходимости.
Конечно, нам пришлось подготовить инструменты для написания таких скриптов: настроенную ide, которой пользуются не только программисты, но и специалисты по внедрению, а также скрипты сборки, скрипты тестирования и тому подобное.
Многое было сделано с помощью Kotlin DSL. Когда вы видите в сюжете «Мы пополнили PayPal на такую-то сумму рублей», то за этой простой строкой описания платежей Здесь скрыто много волшебства, ведь для разных платежей нужно показывать совершенно разные вещи.
Например, когда вы переводите деньги с карты на свой основной счет, для вас главное, чтобы вы сняли деньги с этой карты.
И если вы снимали деньги с чужой привязанной карты для оплаты телефона, вам важно знать, что вы оплатили телефон.
На самом деле волшебства еще больше, и оно очень специфично для каждого конкретного клиента.
Тем более, что клиенты очень разные, у них много разных ограничений и правил.
Например, в некоторых странах логин и номер телефона пользователя нельзя писать нигде в визуальной части.
Поэтому приходится создавать очень специфический код, а в Котлине он пишется в 5 строк, что намного лучше, чем 25 страниц настроек.
Конкретные сценарии также легко сделать на DSL Kotlin — например, блокировка, изменение документов, онбординг.
Поскольку скрипты на оркестраторе — это всего лишь код Колтина, загрузить их из конфигурации не составит труда.
И с помощью такого DSL можно легко описать правила доступа (ABAC) , но об этом чуть позже.
Итак, мы создали несколько различных шаблонов конфигурации, которые образуют язык, и с помощью этого языка мы можем посмотреть на нашу систему и описать, какие типы сервисов и как следует настраивать.
Есть много других точек зрения, с которых можно посмотреть на систему:
- Кастомизация;
- Авторизация/Аутентификация;
- Возможность подключения сервиса;
- Безопасность;
- Высокая доступность;
- Интернационализация, что для нас очень важно и очень очевидно;
- Гарантия качества.
Но я вам расскажу про авторизацию и аутентификацию.
Авторизация/Аутентификация
У нас вроде нормальный набор задач.Достаточно много (10+ миллионов) внешних пользователей и много (10 000) внутренних пользователей.
Более того, мы не знаем инфраструктуру, в которой живут эти внутренние пользователи.
У клиентов может быть Active Directory или может быть какой-то другой поставщик удостоверений — неизвестно.
При этом существуют разные механизмы подтверждения операций.
Некоторым людям необходимо подтвердить некоторые платежные операции с помощью SMS, другим — с помощью Google Authenticator, а в некоторых случаях используется платежный пароль.
Это тоже важная часть авторизации, о которой забывают практически все стандартные решения.
Не говоря уже о разных моделях доступа: ролевых, роли+атрибуты и других.
К сожалению, популярные решения с открытым исходным кодом, такие как Keycloak, не могут удовлетворить этим требованиям.
Они не поддерживают подтверждения отдельных транзакций и их сложно масштабировать для миллионов пользователей.
Нам пришлось сделать трудный выбор (увы, архитектура — это выбор) и реализовать собственное решение для ИАМ/ЦИАМ (Управление идентификацией/Управление идентификацией потребителей).
Но поскольку наше решение соответствует только нашим потребностям, это оказалось не очень сложно.
И теперь по реализованным примитивам аутентификации (сессии, ограничения на точки входа, роли, атрибуты, примитивы для работы с JWT, токены обновления/доступа/запоминания и т.д.) мы можем посмотреть на все сервисы и понять, куда подходит каждый должен быть использован.
Если роли сервиса были распределены правильно, то все остальные точки зрения хорошо вписываются в эти роли.
Мы всегда можем сказать, что, например, для доменных сервисов достаточно только настройки через настройки, аутентификации доступа через проверку корневого сертификата и авторизации через владение.
И при разработке другого подобного сервиса разработчик уже не тратит силы на все эти выборы.
Возможность подключения услуги
Мы работаем в чужой инфраструктуре.У клиента может быть только «голое железо», собственное облако или даже AWS — но наверняка мы можем рассчитывать только на не такой уж экзотический Linux. В этих неизвестных условиях нам необходимо иметь возможность развертывать и поддерживать всю нашу мультисервисную систему, обеспечивая доступность, балансировку, мониторинг и управление всем этим централизованно.
Готовых решений для этого не существует. Почти все хорошие работают только под Kubernetes, например Istio. А всё, что работает не только под Kubernetes, не очень удобно.
Поэтому мы снова сделали свой велосипед — сервис-меш, реализующий сервис-локатор, балансировку нагрузки, Heartbeat, groovy-скрипты и т. д. Он написан на Java+Netty, обеспечивает 13 000 rps на ядро и среднюю задержку примерно 0,4. РС.
Для наших целей — в качестве коляски внутри пода — реализованного функционала абсолютно достаточно.
При необходимости дорабатываем.
Общий
Язык продукта описывает полдюжины типов услуг и несколько шаблонов для всех точек зрения, важных для продукта.Ключевые выборы, сделанные для нашего продукта:
- Kotlin + Foundation DB, а также ClickHouse на стороне;
- Первая разработка API, API описывается на Kotlin, и на его основе мы создаем файлы с OpenApi;
- Kotlin DSL, которые мы активно используем;
- Библиотеки услугам, то есть мы предпочитаем библиотеки услугам.
- Механизм рабочего процесса;
- Решение для IAM/CIAM
- Сервисная сетка;
Все (или почти все) наши технологии:
Основной
Архитектура – это не картинки.Архитектура — это самое важное, что вы хотите сказать о системе.
Я описал вам некоторые подходы к размышлению об архитектуре:
- Архитектура как необходимый выбор;
- Архитектура как язык;
- Архитектура как управление сложностью;
- Архитектура как точки зрения.
- Архитектура как непрерывный процесс;
- Архитектура как организация;
- Архитектура как ограничения;
- Архитектура как гарантия;
Хайлоад++ 2021 - конференция для разработчиков высоконагруженных систем - пройдет 17 и 18 марта 2022 года в «Крокус-Экспо» в Москве.Теги: #платежные системы #архитектура #Высокая производительность #Управление продуктом #kafka #Распределенные системы #Kotlin #микросервисная архитектура #fintech #платежи #foundationdb #cadence #архитектура системы #платежная системаРасписание И Билеты .
В его рамках пройдет Стенд с открытым исходным кодом , где каждый разработчик может рассказать о своем решении.
Вы можете оставить заявку Здесь .
-
Ноутбуки Sony – История И Обзор
19 Oct, 24 -
Ноутбук Aspire-As5742G-373G32Mnkk От Acer
19 Oct, 24 -
Макбук Будущего
19 Oct, 24 -
«В Итоге Я Потерял Три Миллиона Рублей»
19 Oct, 24 -
Почему В Амстердаме Так Много Дата-Центров?
19 Oct, 24 -
Блоги@Mail.ru Снова Обогнали Livejournal
19 Oct, 24 -
Cx_Freeze + Virtualenv = Ошибки И Зигзаги
19 Oct, 24 -
Важность Api Сериализации Вывода
19 Oct, 24