Что следует знать об организации работы при переходе на микросервисную архитектуру
Привет! Меня зовут Руслан Колодяжный, я CTO R&D-центра финтех-компании Wirex. За 5 лет существования наша компания выросла со стартапа до международной платформы, которой пользуются более 3 миллионов людей по всему миру. Одним из важных этапов нашего развития был переход от монолитной архитектуры к микросервисной, подкрепленный новой организационной структурой. В этой статье я расскажу о том, как изменения организационного подхода повлияли на процесс разработки. Возможно, опыт нашей компании будет полезным тем, кто планирует переход на микросервисную архитектуру и хочет сделать этот процесс максимально эффективным.
Предыстория
Все началось с приложения, написанного на .NET Framework. В качестве облачной платформы мы выбрали Azure, как базу данных использовали Azure SQL, а системой контроля версий выступал Bitbucket. В качестве хостинга для API отдали предпочтение Azure App Services. Back-end хостился в виде Azure Cloud Service workers, которые были тесно связаны между собой и являлись монолитом. Разработка back-end велась в одном репозитории командой из 5 человек. Задачей команды было максимально быстро доставить изменения в production и получить обратную связь от пользователей.
Спустя полгода после начала работы количество пользователей выросло до 50 тысяч. Для его увеличения приняли решение расширить функциональность приложения. Число разработчиков увеличилось до 15, и они были разделены на несколько команд. Как методологию разработки мы использовали Feature driven development, фокус разработчиков был сосредоточен на функциональных элементах (features), полезных с точки зрения пользователя. Владение кодовой базой на данном этапе было коллективным (collective code ownership), ответственность команд заключалась в имплементации новой функциональности, а также доставке изменений в production.
Рис. 1. Коллективное владение единой кодовой базой
Коллективное владение кодовой базой, рост количества разработчиков и фокус на скорости релизов приводили к тому, что время на интеграцию кода (слияние изменений) и прохождение регрессионного тестирования увеличивалось.
Каждая из команд имела в своем владении dev environment, которая была максимально похожа на prod. Разработка новой функциональности (feature) для команды из
На пути к изменениям: от монолита к микросервисам
Мы начали всю функциональность максимально переносить из монолитного приложения во вспомогательные или независимые микросервисы. В качестве инфраструктурной платформы был выбран Azure Service Fabric, так как она позволяет быстро настроить кластер и управлять масштабированием. Также Azure Service Fabric предоставляет богатую программную модель в виде stateful/stateless-сервисов, поддержку actor model и reliable state, которые мы использовали в своих микросервисах.
При разделении монолита мы применяли DDD, шаблон Decompose by Subdomain. Количество микросервисов увеличивалось с добавлением в продукт новой функциональности. Каждому микросервису мы выделили свою кодовую базу (репозиторий). При проектировании применялся подход отдельных баз данных (БД) под каждый микросервис. Для разделения ответственности в своем продукте мы использовали CQRS- и Event Sourcing-подходы, shared libraries поставлялись через NuGet.
Также был настроен процесс CI/CD с помощью TeamCity & Octopus. Микросервисы доставлялись в production в виде гранулярных rolling upgrade-деплоев.
Рис. 2. Распределение ответственности команд по микросервисам, коллективное владение распределенной кодовой базой
Распределенная кодовая база исключила все проблемы слияния. Команды больше фокусировались на процессе разработки и в то же время меньше беспокоились о продолжительном регрессионном тестировании. Затраты, выделяемые на масштабирование монолитного приложения, стали существенно ниже. Независимые микросервисы теперь масштабировались по необходимости, и мы могли выдержать нагрузку в 10 раз больше. Это позволило выдерживать пиковые нагрузки при запуске маркетинговых кампаний. Такие действия вместе с маркетинговой стратегией помогли увеличить клиентскую базу до 2 миллионов пользователей.
При разделении на микросервисы особое внимание уделяли безопасности.
Мы смогли реализовать ограничение по доступности своих сервисов на уровне подсетей (Network Security Groups, Demilitarized Zone). Некоторые компоненты, которые владеют информацией security sensitive, вынесены в изолированные кластеры. Вследствие кластеризации мы ежегодно проходим PCI DSS-сертификацию.
Введение strong code ownership и распределение ответственности
Вернемся к организационному аспекту. При разработке новой функциональности каждая из команд могла вносить изменения в любой микросервис и была обязана знать их устройство. В момент, когда общее количество микросервисов превысило 20, возникли трудности с процессом интеграции новичков в команду и обменом знаниями.
Решением стало введение строгого типа владения кодовой базой (strong code ownership) и разделение зон ответственности. Каждый сервис должен был принадлежать только одной команде, которая отвечает за внесение изменений, будущее развитие этого сервиса, его поддержку и доставку в production.
Воплощением strong code ownership в Wirex стали доменные команды, которые появились вместо feature teams. Команды были созданы вокруг доменов — областей знаний, логически объединенных бизнес-процессов и нашего приложения. Их сфера ответственности касалась нескольких микросервисов. Таких команд у нас пять, каждая из них управляет своими микросервисами.
Рис. 3. Распределение ответственности доменных команд по микросервисам, строгий тип владения кодовой базой
Сейчас у каждой доменной команды есть своя development environment, которая максимально приближена по инфраструктуре к production environment.
Появилась полноценная команда DevOps из 8 человек, которая полностью отвечает за инфраструктуру и безопасность. Скорость разработки и количество релизов увеличились примерно в 5 раз.
Как это изменило структуру разработки в компании в целом (не только Back-end)
При поэтапном переходе от монолита к микросервисной архитектуре мы получили важный опыт и поняли, что подход с доменными командами является совершенно другой парадигмой в планировании, реализации и доставке изменений в production и что он должен сопровождаться соответствующими изменениями в организационной структуре. При разработке новой функциональности, кроме разработчиков, в процесс вовлечено много других специалистов: аналитики готовят требования, архитектор определяет зоны ответственности по доменным командам и формирует архитектурный подход, согласованием сроков доставки занимаются координаторы команд.
Рис. 4. Процесс сбора требований и декомпозирования задач на доменную структуру
Доменные команды оценивают задачи и добавляют их в рабочий план (бэклог) в соответствии с приоритетами. Экспертные знания в своих сервисах позволяют им выполнять задачи качественнее и быстрее. В разработке появилась возможность не только накапливать, но и разбирать технический долг, ускорилась доставка сервисов в production. Качественное отличие проявилось в виде более частых гранулярных релизов. Доменные команды делают гранулярные технические релизы микросервисов, затем производят продуктовый релиз включением функциональности с админпанели.
Рис. 5. В бэклогах присутствуют задачи по сервисам, которыми владеет доменная команда
Выводы
Это только небольшая часть описания процесса перехода. Многое осталось за кадром, в том числе и особенности работы ключевого направления команды продуктовой разработки. Наш опыт показал, что для масштабируемости разработки и реализации высоконагруженных систем с помощью микросервисной архитектуры важны не только технические, но и корректные менеджерские решения. При имплементации микросервисной архитектуры нужно учитывать потребность изменений на уровне организационной структуры всей компании, в нашем случае это был переход от collective code ownership к strong code ownership. Чтобы улучшить взаимодействие команд, разработчикам и менеджменту необходимо объединять усилия и улучшать процессы жизненного цикла разработки программного обеспечения.