Disclaimer
Данный проект рассматривается как pet-project. Любую критику и советы готов увидеть в комментариях.
Репозиторий с исходным кодом: https://github.com/theartofdevel/notes_system
Под катом текстовая расшифровка с картинками.
Техническое задание
Давайте придумаем техническое задание и спроектируем систему на бумаге. Первым делом распишем функциональность, чтобы было понятно, что получится в итоге.
Основные возможности:
регистрация и авторизация в системе,
создание/изменение/удаление заметок,
прикрепление файла,
категоризация и тегирование заметок,
полнотекстовый поиск.
Сделаем версию под браузер, приложения на Android и iOS могут появиться в будущем.
Основные страницы UI:
авторизация,
регистрация,
список заметок,
карточка заметки.
Заметки должны поддерживать формат Markdown и состоять из заголовка и тела. В ТЗ намеренно нет нефункциональных требований, таких как производительность, надёжность, катастрофоустойчивость и так далее, чтобы не усложнять систему.
Проектирование системы
Итак, функциональность нам известна, давайте проектировать. Начнём с выбора инфраструктуры.
CI/CD
Собирать систему и деплоить код будем при помощи CircleCI или GitHub Actions. Все сервисы будем упаковывать в Docker-контейнеры и запускать при помощи Docker-compose.
Система контроля версий
Уже есть репозиторий на GitHub. Он будет использоваться как монорепозиторий. Это ускорит разработку и уменьшит количество дополнительной работы с гитом. Весь код будет в свободном доступе под лицензией GPLv3.
Zipkin
Нам нужно дебажить сетевое взаимодействие между сервисами, поэтому мы используем стандарт Opentracing и в качестве реализации возьмем Zipkin. У него много готовых библиотек и для Python, и для Golang.
Search and filter
Для полнотекстового поиска и фильтрации мы возьмём стек ELK, а именно Elasticsearch и Logstash. На данном этапе это как стрелять из пушки по воробьям, вполне можно было организовать этот функционал на хранилище, но:
в качестве обучения и демонстрации возможностей микросервисов это будет отличным примером;
если сервисом начнут пользоваться люди, то мы уже подумали о масштабировании и производительности.
Для наполнения Elasticsearch данными будем настраивать ETL-процессы и использовать Apache Airflow.
Logs
Так как у нас уже есть Elasticsearch, мы будем туда заливать логи всех сервисов. Чтобы отслеживать пересечения логов бизнес-процессов по разным сервисам, будем добавлять в лог TraceId, который нам останется от Zipkin. Логи будем собирать агентом Telegraf или FileBeat.
Service Discovering and Configuration
Для Service Discovering используем Consul и его DNS-сервер. Логика следующая: сервис обращается к другому сервису по хостнейму, а резолвить хостнейм будет Consul.
Для конфигурации всех сервисов будем использовать встроенное в Consul key-value-хранилище. Возьмём утилиту Consul Template. Она будет следить за файлом шаблона и генерировать конфигурацию сервиса, которую он будет использовать при старте, а при изменении значений в Consul Consul Template будет рестартовать сервис.
Сервисы
Теперь посмотрим, какие сервисы у нас будут.
NoteService
Сервис с бизнес-логикой основной сущности. Реализуем основные операции: получение, создание, обновление и удаление. Использовать будем Golang, REST API, формат данных — JSON.
У сервиса будет своё хранилище. Возьмем документоориентированную MongoDB. Она легко масштабируется, формат данных — документ в виде JSON плюс шардирование «из коробки» — опять же пригодится, если будет нагрузка.
FileService
Для управления файлами заметок сделаем отдельный сервис. Скорее всего, это будет совместимое с S3 хранилище MinIO. Файлы будем хранить на файловой системе.
CategoryService
Для управления категориями сделаем отдельный сервис со своим хранилищем. Категории будем хранить в древовидной структуре, поэтому возьмем Neo4j — графовую базу данных. Её основная особенность в том, что коннекты между данными хранятся, а не вычисляются во время запроса. Также она использует язык запросов Cypher, который гораздо проще, чем SQL для написания запросов с неограниченной вложенностью.
Сервис будем делать на языке Python 3 и фреймворке Flask. Асинхронность здесь не так важна, к тому же запускать приложение мы будем при помощи Gunicorn, который сможет распараллелить инстансы сервиса.
APIService
Мы будем делать только веб-приложение без мобильных клиентов, но так как в будущем они могут появиться, нам нужен отдельный сервис с API.
API у нас по большей степени простой CRUD, различного функционала немного, и весь он вращается вокруг заметок. Основных сущностей также мало, поэтому будем готовить обычный REST API.
SearchService
Для полнотекстового поиска сделаем SearchService. Именно он будет обладать доступом в Elasticsearch. Работать с Elasticsearch проще на Python, поэтому выбор очевиден.
UserService
Для пользователей системы будет отдельный сервис также с REST API и хранилищем PostgreSQL.
WebApplication
Для создания веб-приложения используем Vue.js или React.js.
В итоге мы получили такую архитектуру:
Если в системе появятся асинхронные операции, например, отчёты, то мы легко внедрим в эту архитектуру очереди событий.
В следующей части мы начнём разработку системы. Будут показаны основные моменты процесса разработки и конфигурации используемых продуктов.
Комментарии (6)
korsetlr473
10.10.2021 14:28Тоже не понял как всё это будет собираться ?
Если мне нужно на странице конкретного Каталога , сделать поиск по слову в тексте и отсортировать по файлам ?
это запрос с 4м сервисам и джойнами данный в одной функции на бэкэнде ?
dikkini Автор
10.10.2021 14:51всю фильтрацию и поиск по заметкам предполагается делать в ElasticSearch
korsetlr473
10.10.2021 19:00+2в чем тогда смысл сервисов? если у вас в эластике лежит агрегированная полная информация?
тогда просто любой запрос можно сразу по эластику проводить.
Например если я зашел в каталог , выбираются из сервиса записки - записки к каталогу. Почему сразу не сделать запрос к эластику "взять все записки для каталога Х"
dikkini Автор
10.10.2021 19:24распределение нагрузки. тут суть в том, что я пытался в этой системе отразить маленький высоконагруженный проект, где нагрузка должна распределяться между всеми компонентами системы
SubarYan
11.10.2021 12:52Одной MongoDB было бы достаточно. На MongoDB можно и поиск сделать, а такжи и графы хранить.
dandykry
Мне пока ничего непонятно, но ты продолжай!