diff --git a/notificator/software_architecture_document.md b/notificator/software_architecture_document.md index efd553a..f216582 100644 --- a/notificator/software_architecture_document.md +++ b/notificator/software_architecture_document.md @@ -12,10 +12,70 @@ ## Целевая архитектура +### Предполагаемая нагрузка: + +В задании не указана ожидаемая нагрузка, поэтому напишу фанфик. + +Нам известно, что пользователями сервиса будут другие подсистемы. Допустим, изначально такая подсистема будет всего одна, но с достаточно большим числом пользователей -- 100к месячных пользователей. Сервис шлет оповещения каждому из них по 10 раз в день в рабочие часы, особенно активно используя пуши и имейл. 100к умножаем на 10 и делим на число секунд в рабочих часах, 1млн / (8 * 60 * 60) -- получается около 34 rps и например 50 rps в пике. +Через полгода собираются заехать еще два сервиса со сходным числом пользователей. Постараемся подготовиться к нагрузкам втрое больше ~150 rps. + +Для пользователей мы должны хранить настройки оповещений и контактные данные. Настройки оповещений скорее всего будут меняться не очень часто, а вот читать эти настройки нужно будет при каждом запросе на отправку оповещения, значит, система будет Read intensive. ### Диаграмма контекста (C1): ![C1](static/c1.svg) Предполагается, что любой из существующих компонентов может отправить оповещение пользователю. При этом существующие каналы связи и предпочтения по их использованию известны системе оповещений, компоненты остальной системы знать это не должны. ### Диаграмма контекста (C2): -> Вам нужно добавить C2 и C3 диграммы системы. Если при переходе от уровня к уровню были приняты архитектурно-значимые решения (выбор технологии для хранилища, дополнительные дизайн-паттерны и тд.) нужно добавить ссылки на соответствующие ADR. + +![C2](static/c2.svg) + +Система будет состоять из следующих модулей: + +* API Gateway -- входная точка для клиентов системы. Осуществляет аутентификацию и роутинг запросов. Для этого используем Nginx. + https://nginx.org/en/docs/http/ngx_http_auth_request_module.html#auth_request + https://nginx.org/en/docs/http/ngx_http_proxy_module.html + +* Load Balancer -- Nginx https://nginx.org/en/docs/http/load_balancing.html + +- Golang сервер управления отправкой сообщений, который умеет + - получать сообщения от других систем через системный интерфейс + - позволяет отправлять сообщение по всем каналам группе клиентов + - позволяет отправлять сообщение адресно в каждый канал связи конкретным адресатам + - добавляет сообщения в очередь на отправку согласно настройкам каналов + - менять настройки отправления сообщений в реальном времени + Документация по использованию network API в go https://pkg.go.dev/net@go1.21.3 + +- Хранилище -- база данных PostgreSQL, которая хранит информацию об учетных записях пользователей из других подсистем и настроек сообщений для них + +- Очередь сообщений RabbitMQ для отправки в разных каналах. Позволяет обрабатывать сообщения в порядке очереди и учитывать пропускные лимиты каналов. + https://learn.microsoft.com/en-us/azure/architecture/patterns/queue-based-load-leveling + https://www.rabbitmq.com/queues.html + +- Компоненты для предобработки и отправки сообщений: + - SMS: + предусматривает ограничение количества символов в СМС + - Email: + предусматривает возможность попадания email в спам + - Push: + предусматривает возможность отправки пушей на разные платформы (iOS, Android, Web) + Вряд ли есть потребность целиком реализовывать эти сервисы, можно взять внешние решения и добавить адаптеры для них. + +### Architecture Decision List: +|ID|Дата|Статус|Имя|Решение| +|---|---|---|---|---| +|[ADR-1](static/adr/adr-1.md)|08.10.23|Принято|Полина Шестакова|Модульная архитектура| +|[ADR-2](static/adr/adr-2.md)|08.10.23|Принято|Полина Шестакова|Сервер для управления отправкой сообщений| +|[ADR-3](static/adr/adr-3.md)|08.10.23|Принято|Полина Шестакова|База данных для хранения настроек отправки| +|[ADR-4](static/adr/adr-4.md)|08.10.23|Принято|Полина Шестакова|Брокер сообщений| +|[ADR-5](static/adr/adr-5.md)|08.10.23|Принято|Полина Шестакова|API Gateway и Load Balancer| +|[ADR-6](static/adr/adr-6.md)|08.10.23|Принято|Полина Шестакова|Логирование и мониторинг| + +### Диаграмма компонентов (C3): + +![C3](static/c3.svg) +### Критерии оценки прототипа: + +1. Оповещения доходят до адресатов по всем поддержанным каналам +2. Настройки оповещений позволяют ограничивать отправку +3. Микросервисная архитектура позволяет легко отключать и включать каждый из каналов +4. Система выдерживает ожидаемую нагрузку, включая пиковую \ No newline at end of file diff --git a/notificator/static/adr/adr-1.md b/notificator/static/adr/adr-1.md new file mode 100644 index 0000000..7ffe96b --- /dev/null +++ b/notificator/static/adr/adr-1.md @@ -0,0 +1,32 @@ +#### ID: ADR-1 + +#### Дата: 08.10.23 + +#### Статус: Принято + +#### Участники: +Полина Шестакова + +#### Решения: +Модульная архитектура, которая разделяет задачи и позволяет легко расширять функциональность. + +Для контейнеризации компонентов используем Docker. Это популярное решения с большой поддержкой коммьюнити. В дальнейшем для оркестрации системы может использоваться Kubernetes, но на данный момент это необязательно. + +Альтернатива здесь, это использовать монолитную архитектуру, но число разных каналов для отправки оповещений наводит на мысль, что компоненты для этих каналов должны быть независимы и иметь возможность легкого отключения и подключения. Микросервисы лучше решат данную задачу. + +#### Контекст: +Нам необходимо разработать систему электронной почты, которая позволяет отправлять сообщения по разным каналам связи -- смс, пуш, имейл. Система должна предусматривать возможность интеграции с другими системами с помощью API. Также система должна принимать от пользователя системы настройки по каждому каналу связи для рассылки уведомлений (вкл./выкл., адрес/номер/логин) и учитывать их при последующих рассылках. + +#### Последствия: + +#### Положительные: +* позволяет легко добавлять новые функции, например, новые каналы связи +* позволяет масштабировать систему при росте нагрузки, добавляя новые сервера для отправки сообщений +* отказоустойчивость системы - выход из строя одного компонента не обязательно приводит к полной остановке системы + +#### Отрицательные: +* требует больше времени на разработку +* требует больше ресурсов для сопровождения и поддержки +* нужна команда разработки с опытом разработки микросервисной архитектуры + + diff --git a/notificator/static/adr/adr-2.md b/notificator/static/adr/adr-2.md new file mode 100644 index 0000000..028c11d --- /dev/null +++ b/notificator/static/adr/adr-2.md @@ -0,0 +1,33 @@ +#### ID: ADR-2 + +#### Дата: 08.10.23 + +#### Статус: Принято + +#### Участники: +Полина Шестакова + +#### Решения: +Сервер для управления отправкой сообщений +Выбираем golang + +Альтернативные решения: +python + django -- более распространенный, но менее производительный +nodejs + express -- поддерживает асинхронное выполнение из коробки, но менее производительный и не поддерживает "настоящую" многопоточность. менее распространенный чем go и python + +#### Контекст: +Необходимо написать сервер, который будет получать сообщения от других сервисов и управлять их рассылкой по заданным в базе данных каналам, используя для этого брокер сообщений. + +#### Последствия: + +#### Положительные: +* go поддерживает треды, что позволит увеличить пропускную способность системы +* наличие встроенных инструментов для работы с сетью +* кроссплатформенный +* хорошая документация +* большое коммьюнити + +#### Отрицательные: +* отсутствие стандартных библиотек для работы с базами данных +* у команды небольшой опыт разработки на этом языке, потребуется дополнительное время на обучение и наем новых сотрудников +* довольно новый язык, не для всех задач есть готовые библиотеки diff --git a/notificator/static/adr/adr-3.md b/notificator/static/adr/adr-3.md new file mode 100644 index 0000000..15f1e1f --- /dev/null +++ b/notificator/static/adr/adr-3.md @@ -0,0 +1,29 @@ +#### ID: ADR-3 + +#### Дата: 08.10.23 + +#### Статус: Принято + +#### Участники: +Полина Шестакова + +#### Решения: +База данных для хранения настроек отправки +Выбираем PostgreSQL + +Альтернативные решения: +MySQL -- легче в настройке, но имеет меньше инструментов для работы с данными. +MongoDB -- NoSQL база данных. Может быть полезна, если в будущем понадобится хранить большое количество данных, но в нашем случае структура у данных достаточно жесткая, чтобы использовать SQL базу данных + +#### Контекст: +Нужно хранить настройки отправки сообщений для каждого пользователя и канала. Некоторые пользовательские данные будут забираться из других подсистем. По описанию задачи выглядит так, что данные достаточно структурированы -- у каждого пользователя есть набор контактных данных и настройки оповещений. + +#### Последствия: + +#### Положительные: +* бесплатное open source решение +* масштабируемость +* большое число фичей и инструментов для работы с данными + +#### Отрицательные: +* сложность настройки diff --git a/notificator/static/adr/adr-4.md b/notificator/static/adr/adr-4.md new file mode 100644 index 0000000..1e44d62 --- /dev/null +++ b/notificator/static/adr/adr-4.md @@ -0,0 +1,33 @@ +#### ID: ADR-4 + +#### Дата: 08.10.23 + +#### Статус: Принято + +#### Участники: +Полина Шестакова + +#### Решения: +Брокер сообщений +Выбираем RabbitMQ + +Альтернативные решения: +Kafka - не реализует маршрутизацию сообщений из коробки +ActiveMQ - сложнее в использовании, а документация не такая подробная, как для RabbitMQ. + +#### Контекст: +Нужна очередь для отправки сообщений, чтобы + - не блокировать сервер отправки сообщений в случае, если какой-то из каналов связи недоступен + - учитывать пропускные лимиты каналов связи + - обеспечить переотправку сообщений + +#### Последствия: + +#### Положительные: +* прост в настройке и использовании +* хранит очередь сообщений +* может сохранять очередь даже в случае перезапуска процесса + +#### Отрицательные: +* нужно получить дополнительную экспертизу по использованию этого инструмента +* сложно реализовать горизонтальное масштабирование diff --git a/notificator/static/adr/adr-5.md b/notificator/static/adr/adr-5.md new file mode 100644 index 0000000..399780a --- /dev/null +++ b/notificator/static/adr/adr-5.md @@ -0,0 +1,27 @@ +#### ID: ADR-5 + +#### Дата: 08.10.23 + +#### Статус: Принято + +#### Участники: +Полина Шестакова + +#### Решения: +API Gateway и Load Balancer +Выбран Nginx, так как его можно использовать в качестве API Gateway для маршрутизации и балансировки нагрузки между микросервисами. + +#### Контекст: +Так как в качестве архитектуры были выбраны микросервисы, понадобится балансировщик нагрузки. Также нужен API Gateway для обеспечения безопасности и аутентификации пользователей -- других микросервисов. +В качестве метода аутентификации можно остановиться на API Key. Для начала список ключей можно хранить прямо в конфигурации шлюза. + +#### Последствия: + +#### Положительные: +* open source решение +* подробная документация +* легкость масштабирования + +#### Отрицательные: +* требует дополнительных затрат на интеграцию и поддержку + diff --git a/notificator/static/adr/adr-6.md b/notificator/static/adr/adr-6.md new file mode 100644 index 0000000..03e629a --- /dev/null +++ b/notificator/static/adr/adr-6.md @@ -0,0 +1,29 @@ +#### ID: ADR-6 + +#### Дата: 08.10.23 + +#### Статус: Принято + +#### Участники: +Полина Шестакова + +#### Решения: +Логирование и мониторинг +Для получения логов можно использовать syslog, а для визуализации и алертов -- grafana. + +Альтернативные решения: +Sentry -- популярный инструмент для логирования и алертов, хороший запасной кандидат. + +#### Контекст: +Нужно собирать, хранить и обрабатывать логи работы сервиса. +Например, в случае, если отправка сообщения не увенчалась успехом даже после максимального числа ретраев, хочется сразу же понимать, насколько это частый случай, нет ли проблемы с определенным пользователем, каналом или инстансом сервиса. + +#### Последствия: + +#### Положительные: +* удобный интерфейс для просмотра логов + +#### Отрицательные: +* дополнительные затраты на интеграцию логирования и алертов +* затраты на создание инфраструктуры для хранения логов +* затраты на поддержку и сопровождение инфраструктуры diff --git a/notificator/static/c2.svg b/notificator/static/c2.svg new file mode 100644 index 0000000..50af656 --- /dev/null +++ b/notificator/static/c2.svg @@ -0,0 +1,1267 @@ +Диаграмма контейнеров для сервиса рассылки сообщенийNotifier[System]API Gateway[Nginx] Осуществляетаутентификацию ибалансировку нагрузкиСервис рассылкиоповещений[Golang server] Управляет отправкойоповещений, даетвозможность настройки имультиплексированияотправкиБрокер сообщений[RabbitMQ] Хранит очередь сообщений,контролирует ширинуканалов, ретраи и доставкуКомпонент дляпредобработки иотправки SMS[Golang server]Компонент дляпредобработки иотправки Email[Golang server]Компонент дляпредобработки иотправкиPush-нотификаций[Golang server]Database[PostgreSQL] Хранит настройкиуведомлений каждогопользователяОтправительоповещений Компонент системы,отправляющий оповещение.Хранит основныепользовательские данныеСистема отправки EmailсообщенийСистема отправкиPush-нотификацийСистема отправки SMSПрисылает оповещениес указанием каналовотправкиМеняет настройкиоповещений дляадресатаПеренаправляетзапросы на отправкуОтправляет сообщения вочередьОтправляет сообщенияна предобработкуОтправляет сообщенияна предобработкуОтправляет сообщенияна предобработкуОтправляет сообщения всистему отправки EmailОтправляет сообщения всистему отправкиPush-нотификацийОтправляет сообщения всистему отправки SMSПолучает и сохраняетнастройки оповещенийадресатаLegend personsystemcontainerexternal personexternal systemexternal container \ No newline at end of file diff --git a/notificator/static/c3.svg b/notificator/static/c3.svg new file mode 100644 index 0000000..5d93611 --- /dev/null +++ b/notificator/static/c3.svg @@ -0,0 +1,1353 @@ +Диаграмма компонента сервиса рассылки оповещенийNotifier Application[Container]Контроллер установкинастроек[MVC Rest Controller] Позволяет отправителюустановить настройки дляотправки оповещенийКонтроллер приемаоповещений[MVC Rest Controller] Позволяет отправителюотослать оповещение суказанием каналов ипользователейКомпонент для отсылкиоповещений[Golang app] Добавляет оповещения вочередь согласно заданнымнастройкам и параметрамотправкиБрокер сообщений[RabbitMQ] Хранит очередь сообщений,контролирует ширинуканалов, ретраи и доставку.Database[PostgreSQL] Хранит настройкиуведомлений каждогопользователя.Отправительоповещений Компонент системы,отправляющий оповещение.Хранит основныепользовательские данныеAPI Gateway[Nginx] Осуществляетаутентификацию с помощьюAPI Key и балансировкунагрузкиWrites toUsesUsesReads fromPushes messagesJSON/HTTPS + API KeyJSON/HTTPSJSON/HTTPSLegend personsystemcontainercomponentexternal personexternal systemexternal containerexternal component \ No newline at end of file diff --git a/notificator/static/puml/c2.puml b/notificator/static/puml/c2.puml new file mode 100644 index 0000000..87621fb --- /dev/null +++ b/notificator/static/puml/c2.puml @@ -0,0 +1,38 @@ +@startuml + +!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml + +LAYOUT_WITH_LEGEND() + +title Диаграмма контейнеров для сервиса рассылки сообщений + +Person(sender_app, "Отправитель оповещений", "Компонент системы, отправляющий оповещение. Хранит основные пользовательские данные") + +System_Boundary(c1, "Notifier") { + Container(gateway, "API Gateway", "Nginx", "Осуществляет аутентификацию и балансировку нагрузки") + Container(notifier_app, "Сервис рассылки оповещений", "Golang server", "Управляет отправкой оповещений, дает возможность настройки и мультиплексирования отправки") + Container(message_queue, "Брокер сообщений", "RabbitMQ", "Хранит очередь сообщений, контролирует ширину каналов, ретраи и доставку") + Container(sms_processor, "Компонент для предобработки и отправки SMS", "Golang server", "") + Container(email_processor, "Компонент для предобработки и отправки Email", "Golang server", "") + Container(push_processor, "Компонент для предобработки и отправки Push-нотификаций", "Golang server", "") + ContainerDb(database, "Database", "PostgreSQL", "Хранит настройки уведомлений каждого пользователя") +} + +System_Ext(email_system, "Система отправки Email сообщений") +System_Ext(push_system, "Система отправки Push-нотификаций") +System_Ext(sms_system, "Система отправки SMS") + +Rel(sender_app, gateway, "Присылает оповещение с указанием каналов отправки") +Rel(sender_app, gateway, "Меняет настройки оповещений для адресата") +Rel(gateway, notifier_app, "Перенаправляет запросы на отправку") +Rel_Neighbor(notifier_app, message_queue, "Отправляет сообщения в очередь") +Rel(message_queue, email_processor, "Отправляет сообщения на предобработку") +Rel(message_queue, push_processor , "Отправляет сообщения на предобработку") +Rel(message_queue, sms_processor, "Отправляет сообщения на предобработку") +Rel(email_processor, email_system, "Отправляет сообщения в систему отправки Email") +Rel(push_processor, push_system, "Отправляет сообщения в систему отправки Push-нотификаций") +Rel(sms_processor, sms_system, "Отправляет сообщения в систему отправки SMS") + +Rel(notifier_app, database, "Получает и сохраняет настройки оповещений адресата") + +@enduml diff --git a/notificator/static/puml/c3.puml b/notificator/static/puml/c3.puml new file mode 100644 index 0000000..25ce196 --- /dev/null +++ b/notificator/static/puml/c3.puml @@ -0,0 +1,31 @@ +@startuml + +!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Component.puml + +LAYOUT_WITH_LEGEND() + +title Диаграмма компонента сервиса рассылки оповещений + +Container(message_broker, "Брокер сообщений", "RabbitMQ", "Хранит очередь сообщений, контролирует ширину каналов, ретраи и доставку.") +ContainerDb(db, "Database", "PostgreSQL", "Хранит настройки уведомлений каждого пользователя.") + +Person(sender_app, "Отправитель оповещений", "Компонент системы, отправляющий оповещение. Хранит основные пользовательские данные") +Container(gateway, "API Gateway", "Nginx", "Осуществляет аутентификацию с помощью API Key и балансировку нагрузки") + +Container_Boundary(api, "Notifier Application") { + Component(settings, "Контроллер установки настроек", "MVC Rest Controller", "Позволяет отправителю установить настройки для отправки оповещений") + Component(send_to, "Контроллер приема оповещений", "MVC Rest Controller", "Позволяет отправителю отослать оповещение с указанием каналов и пользователей") + Component(sender, "Компонент для отсылки оповещений", "Golang app", "Добавляет оповещения в очередь согласно заданным настройкам и параметрам отправки") + + Rel(settings, db, "Writes to") + Rel(settings, sender, "Uses") + Rel(send_to, sender, "Uses") + Rel(sender, db, "Reads from") + Rel(sender, message_broker, "Pushes messages") +} + +Rel(sender_app, gateway, "JSON/HTTPS + API Key") +Rel(gateway, settings, "JSON/HTTPS") +Rel(gateway, send_to, "JSON/HTTPS") + +@enduml