Всем привет! Мы — ребята из команды разработки спецпроектов Тинькофф: Гена, Кирилл, Витя, Миша, Никита и Леша. Команда спецпроектов разработала и запустила проекты «Монополия Тинькофф», «5 букв», «Пока, пакет» и многие другие. Мы расскажем о том, как создаем благотворительные проекты и почему процесс сбора заявок не так прост, как кажется.
Поделимся, как разрабатывали систему приема и оценки заявок для грантового конкурса, какие библиотеки для валидации и моделирования статусной модели заявки использовали. Расскажем, как проектирование архитектуры без архитектора помогает нам прокачивать разработчиков и динамично реагировать на требования бизнеса.
Поддержка будет
Цель проекта — поддержать благотворительные организации. Конкурс проводится с 2021 года, и в нем поучаствовали больше тысячи НКО. Для участия в проекте фонды оставляли заявки на сайте проекта, а независимые эксперты из некоммерческого сектора оценивали заявки, выбирали самые актуальные, предоставляли помощь и контролировали целевое использование денежных средств.
Проект «Поддержка будет» — не единственный наш грантовый проект. Второй год подряд мы запускаем проект «Скиньте мяч», в рамках которого выделяем гранты на реализацию социальных инициатив в городах по всей России. Проекты похожи по функциональным и нефункциональным требованиям, а различаются тем, что в проекте «Поддержка будет» принимают участие НКО, а в проекте «Скиньте мяч» — физические лица.
Работа с требованиями
Техническая реализация проекта «Поддержка будет» может показаться довольно простой — это сайт, на котором можно заполнить заявку для участия или посмотреть результаты предыдущих запусков проекта.
В дополнение к этому проект должен:
собирать и безопасно хранить заявки;
предоставлять интерфейс для безопасной и непредвзятой модерации и оценки заявок;
хранить историю действий модераторов и экспертов;
перезапускаться в минимальные сроки и с минимальными издержками на разработку.
На первом этапе мы определились с функциональными и нефункциональными требованиями. В разработке требований у нас участвуют менеджер проекта, фронтенд- и бэкенд-разработчики и тестировщик. Мы намеренно отказались от роли архитектора, потому что в условиях динамичной разработки проектов архитектор становится бутылочным горлышком, а его ошибка стоит очень дорого.
Задачи с высокой степенью неопределенности требуют быстрого и профессионального реагирования на местах, поэтому мы сделали акцент на T-shape-развитии разработчиков. Наши ребята способны самостоятельно проектировать инфраструктуру и решать сложные задачи.
Мы работаем в небольших продуктовых командах, поэтому нам легко закрепить зоны ответственности, а конфлюенс как единый источник истины о системе и оперативная коммуникация внутри команды позволяют быстро подстраивать проектируемую систему под требования бизнеса.
В контексте проекта «Поддержка будет» наша команда сконцентрировалась на таких нефункциональных требованиях:
data retention — хранение персональных данных в соответствии с юридическими ограничениями;
security — безопасная авторизация пользователей/админов/экспертов, разграничение прав и доступов в систему;
auditability — возможность системы ответить на вопросы, кто, что и когда делал с данными;
flexibility — возможность системы адаптироваться к меняющимся требованиям.
Чтобы удовлетворить все требования, нам потребовалось реализовать веб-приложение для приема заявок на конкурс, веб-приложение для модерации и оценки заявок и приложение для сохранения заявок, безопасного доступа к данным и авторизации пользователей, админов и экспертов.
Система сбора и оценки заявок
Наивная реализация сбора заявок на конкурс сводится к гугл-форме. Но такой вариант неприемлем с точки зрения безопасности пользовательских данных и удобства оценки заявок.
Простая задача по сбору и хранению заявок осложняется большим количеством персональных данных в заявке. Важное требование — прозрачность процесса оценки заявок и безопасная передача ПД экспертам из профильных организаций. А еще оценка 500+ заявок для каждого конкурса требует времени, а значит, дополнительных расходов, поэтому в наших интересах сделать процесс максимально быстрым, удобным и прозрачным. Проанализировав требования, мы решили спроектировать и разработать собственную систему для приема и оценки заявок.
Первый элемент системы — веб-приложение для сбора заявок. Мы подключили интеграцию с Tinkoff ID, чтобы сделать вход в приложение простым и безопасным. Дизайн веб-приложения тоже не вызвал трудностей. Задачу создать дружелюбный интерфейс с референсами к айдентике Тинькофф ребята-дизайнеры выполнили на отлично. Следующим шагом был выбор безопасного и эффективного хранилища данных.
База данных для хранения заявок
Мы развиваем собственные облачные технологии и умеем предоставлять внутренним пользователям множество решений по модели Software as a Service. К таким решениям относится и Database as a Service. Для хранения пользовательских данных мы использовали PostgreSQL, предоставляемый по модели DBaaS, чтобы наши пользователи не переживали за сохранность и безопасность хранения данных.
Преимущества от использования базы данных по модели DBaaS:
банковские инструменты SRE/SLA;
банковские инструменты Observability;
хранение персональных данных в соответствии с законодательством РФ;
отказоустойчивое и проверенное решение.
Это очень важные преимущества, когда речь идет о долгосрочной поддержке проекта. Нам необходимо хранить данные надежно и безопасно, а еще получать оперативную обратную связь о состоянии инфраструктуры.
Для реализации формы мы использовали UI Kit платформы Тинькофф, чтобы подчеркнуть преемственность интерфейсов и переиспользовать гибкую палитру React-компонентов платформы от простых инпутов до сложных календарей с интервалами дат. У нас пока нет возможности поделиться UI Kit под React, но можно посмотреть возможности опенсорсной реализации UI Kit под Angular.
Наиболее приоритетными требованиями к форме были:
гибкость к изменениям в требованиях;
скорость синхронизации схемы валидации данных;
сохранение промежуточного состояния заявки в БД;
целостность данных при сохранении заявки.
Решение этой задачи прошло несколько итераций. Первый же запуск проекта показал, что верстка формы с помощью formik или react-hook-form требует слишком много времени. Требования бизнеса могут стремительно меняться. Нужно быстро удалять или добавлять поля формы, при этом успевать синхронизировать валидацию данных на бэкенде и фронтенде.
Синхронизация схемы данных с помощью JSON-схемы
Синхронизация схемы валидации данных отнимала время, потому что каждая задача на изменение полей формы включала как минимум двух разработчиков — изменить на бэкенде, изменить на фронтенде. Ребята-разработчики предложили использовать JSON-схему для валидации данных заявки на бэкенде и для верстки формы в веб-приложении.
Первым решением стала библиотека react-jsonschema-form, которую мы внедрили в админку на первом запуске проекта. Библиотека доказала, что JSON-схема интуитивно понятна и ее структуру несложно изучить. UI-схема казалась избыточной, а подключить к библиотеке react-jsonschema-form UI Kit Тинькофф было непросто.
Мы решили написать собственную реализацию, которая не поддерживает рекурсивный рендеринг, но имеет согласованную с дизайнерами стилизацию и структуру. Для этого взяли formik и написали вокруг него библиотеку с множеством валидаторов, поддержкой кастомных сообщений об ошибках, валидацией на onBlur или onSubmit, поддержкой полей типа Date, File, Range и другими опциями, которые кастомизируются за пару секунд с помощью JSON-схемы.
// Часть JSON-схемы для рендера формы в веб-приложении
// и валидации данных в NodeJS-приложении
{
// ...
organizationInn: {
formRowParams: {
title: 'Информация об организации',
sectionId: SectionId.UserOrganization,
},
type: 'string',
fieldType: 'plane',
initial: '',
validators: {
isRequired: true,
minLength: 10,
maxLength: 10,
},
errorMessages: {
isRequired: 'Заполните поле',
minLength: 'ИНН должен состоять из 10 цифр',
maxLength: 'ИНН должен состоять из 10 цифр',
},
component: {
isNumber: true,
type: 'dadata-company-autocomplete-suggestion',
label: 'ИНН',
placeholder: '7710140679',
dataQA: 'organization-inn',
pathToFieldValueInSuggestion: 'data.inn',
showCompanyNameInSuggestion: true,
fieldToAutocompleteMap: {
organizationOgrn: 'data.ogrn',
organizationName: 'value',
organizationLegalAddress: 'data.address.value',
['fundHead.name']: 'data.management.name',
},
},
},
// ...
}
Следующим шагом стал перенос JSON-схемы на бэкенд и валидация данных заявки с помощью пакета ajv. Мы решили хранить схему в базе данных, чтобы безрелизно вносить в нее правки. Так мы получили единый источник истины о структуре заявки.
Преимущества выбранного подхода:
для изменения полей формы не нужно ставить задачу на фронтенд- и бэкенд-разработчика. С задачей справится, причем очень быстро, всего один человек;
не нужно синхронизировать валидацию данных заявки на бэкенде и поля в форме на фронтенде. Валидация на бэкенде и форма всегда синхронны;
существенно возросла скорость перезапуска проекта и уменьшились затраты на разработку.
Следующий шаг — модерация и оценка.
Веб-приложение для модерации и оценки заявок
С первого запуска проекта мы инвестировали время в проектирование и разработку веб-приложения для модерации и оценки заявок.
Чтобы наши пользователи не переживали за сохранность ПД и непредвзятую оценку заявки, а мы экономили время на оценке заявок на каждом перезапуске проекта, приложение должно включать функциональности:
первичная ручная модерация заявок до назначения на экспертов;
автоматическое распределение заявок на экспертов по профессиональным навыкам экспертов в зависимости от категории НКО;
распределение как минимум на двух экспертов; если оценки обоих экспертов не совпали, то назначение третьего;
распределение доступов между экспертами — эксперт видит только назначенные на него заявки;
логирование всех действий модераторов и экспертов в системе.
После сохранения в БД заявке присваивается статус. Каждое действие с заявкой переводит ее в новый статус: «На модерации», «Ожидает оценки» и т. д. Это классический конечный автомат. Для его проектирования мы использовали инструмент stately.ai и библиотеку xstate.
Stately.ai позволяет прямо в веб-интерфейсе прогонять заявку по статусам и выявлять недостающие переходы или deadlock-и. С помощью stately.ai мы спроектировали статусную модель, а затем реализовали ее в коде и адаптировали интерфейс веб-приложения.
Для модерации и оценки заявок требуется открыть внешним экспертам доступ к заявкам. При этом заявки содержат большое количество ПД, поэтому просто рассылать экспертам файлы с заявками нельзя. А еще эксперт должен иметь доступ только к тем заявкам, что назначены на него для оценки, чтобы его оценка была максимально беспристрастной.
На оценку каждой заявки назначаются минимум два эксперта, чтобы исключить субъективную оценку, а если оценки не совпадают (один отклонил, второй одобрил), то автоматически назначается третий эксперт.
Tinkoff ID позволил нам переиспользовать авторизацию из основного веб-приложения и в относительно короткие сроки интегрировать механизм продления сессии для пользователей, модераторов и экспертов.
Для разграничения доступов мы просто использовали белый список идентификаторов модераторов/экспертов. Интеграция с Tinkoff ID помогла оставить внешних экспертов с доступом только к одной конкретной системе — веб-приложению для оценки заявок. Никакие другие доступы внешним подрядчикам не выдавались. В то же время мы подключили логирование каждого действия экспертов, чтобы исключить любые попытки сговора или некорректной оценки заявки.
На этапе проектирования интерфейса нам снова помог UI Kit Тинькофф. С помощью него фронтенд-разработчик быстро собрал таблицу для отображения списка заявок с асинхронной пагинацией.
Максимальной унификации мы достигли с подключением JSON-схемы для отображения данных заявки, потому что безрелизная правка схемы вносит изменения в форму заявки у пользователей, одновременно та же схема используется для валидации данных в NodeJS-приложении и для отображения данных заявки у модераторов/экспертов.
Заключение
В плане функциональных и нефункциональных требований у спецпроекта «Поддержка будет» много ограничений из-за персональных данных пользователей в заявке. Поэтому мы спроектировали и успешно реализовали систему для приема, модерации и оценки заявок.
Использование JSON-схемы позволило безрелизно синхронизировать все элементы системы, а это уменьшило количество рутинных задач для разработчиков. Благодаря stately.ai и xstate мы спроектировали статусную модель заявки без deadlock-ов, а интеграция с готовыми решениями, такими как Tinkoff ID и Tinkoff UI Kit, значительно ускорила разработку. И главное, благодаря горизонтальному подходу к дизайну мы успешно реализовали все функциональные и нефункциональные требования.
В итоге у нас получилась автоматизированная и безопасная система для модерации и оценки заявок, которая отлично себя показала на грантовых проектах «Поддержка будет» и «Скиньте мяч».
А самое ценное, что работа над такими проектами — это не только профессиональный челлендж, но и понимание, что ты помог сделать жизнь людей лучше.
Расскажите про ваши кейсы и пишите вопросы в комментариях!