Введение
Что такое nice-pea-chat - Полноценный мессенджер - приложение, сервер и обвязка
Цель документа - Составить общую картину, наращивать функционал требуемый функционал и не сбиться🦥
Область применения - Нет
Глоссарий
ключевые термины и сокращения
Предметная область - Часть реального мира, рассматриваемая в пределах данного контекста
Сущность - Описание объекта предметной области
Модель - Сущность описанная в коде
Обзор системы
Архитектурный стиль - service-oriented architecture(SOA).
Высокоуровневая диаграмма компонентов системы
todo
Детальный дизайн
Компоненты системы
Описание каждого значимого компонента:
Назначение
Ответственность
Интерфейсы (API, события)
Зависимости
API Design
Принципы проектирования (REST, GraphQL, gRPC)
Важные эндпоинты с примерами
Схемы данных (DTO, модели)
Аутентификация/авторизация
Модель данных
ER-диаграмма или схема данных
Стратегия миграций (если применимо)
Описание сущностей и их отношений
Чат
Структура:
- Идентификатор
- Название чата - может меняться, неуникальное
- идентификатор главного администратора - может меняться
Интеграции
Внешние системы и протоколы взаимодействия
Форматы сообщений/событий
Схемы интеграции (синхронная/асинхронная)
Нефункциональные требования
Масштабируемость
Горизонтальное/вертикальное масштабирование
Надежность
Стратегии обработки ошибок
Механизмы retry
Резервирование
Операционные аспекты
Логирование (формат, уровни, ротация)
Мониторинг (метрики, алерты)
Развертывание (CI/CD, стратегии)
Резервное копирование
Приложения
Диаграммы (последовательностей, состояний и т.д.)
Ссылки на дополнительные материалы
- Репозиторий содержит код для открытого урока "Архитектура проекта на Go"
- Репозиторий с демонстрирующий структуру основанную чистой архитектуре - Example of Clean Architecture + DDD in go
- Что же такое «Модель предметной области»? - https://habr.com/ru/articles/453906
- Разбор архитектурных паттернов на простом примере - Разбираем на практике blog на Symfony
- Блеск и нищета модели предметной области - https://habr.com/ru/companies/jugru/articles/503868/
- Clean Architecture template for Golang services - https://github.com/evrone/go-clean-template
- Uber Go Style Guide - https://github.com/uber-go/guide/blob/master/style.md
- Как работать с Postgres в Go: практики, особенности, нюансы - https://habr.com/ru/companies/oleg-bunin/articles/461935/
Makefile
В этом проект для упрощения и автоматизации рутинных задач разработки используется Makefile
. Ниже приведён список доступных целей (targets) и их описание.
Доступные цели
https://github.com/nice-pea/npchat
Цель | Описание |
---|---|
test | Запустить тесты Go во всём проекте. |
vet | Запустить утилиту go vet , для статического анализа. |
lint | Запустить линтер (golangci-lint ), на всего - 1 количестве ядер. |
check | Запуск vet и lint . Рекомендуется для проверки перед коммитом или PR. |
run | Запустить приложение (cmd/npc/main.go ). |
mdbook | Собирать mdbook в режиме watch и открыть в браузере по умолчанию. |
mdbook-build | Собирать mdbook. |
https://github.com/nice-pea/npchat-docs
Цель | Описание |
---|---|
open | Собирать mdbook в режиме watch и открыть в браузере по умолчанию. |
build | Собирать mdbook. |
Как использовать
Чтобы выполнить какую-либо цель, просто введите в терминале:
make <цель>
Например:
make test
или
make check
Соглашение по git
Формат коммитов в master
Название коммита:
[номер задачи из таск-трэкера] [Название задачи] (#[номер PR с правкой по задаче])
Пример:
50078921 Добавить регистрацию пользователей (#42)
Если коммит большой, к нему стоит добавить описание, значительные изменения разделять новой строкой, начинающейся с *
:
* Обновлен способ обработки ошибок в роутере
* Стандартный роутер заменен на "libname"
Формат веток
В названии ветки указывать автора, а для разделения слов использовать минусы:
[автор]/[номер задачи из таск-трэкера]-[Название-задачи]
Пример:
saime/50078921-Добавить-регистрацию-пользователей
Формат коммитов в ветки авторов
Формат коммитов в ветки автором не установлен
1. Дизайн системы
1.1 Функционал
1.1.1 Клиент - мобильное приложение (android)
a.k.a mobile app
Приложение -
- Если запрос прошел с ошибкой (не 500), то текст из ответа надо вывести на экрана
- Для проверки поддержки апи, приложение отправляет на сервер запрос, в случае неудачи которого, открывается экран с ошибкой версии api
- Если сервер вернет ошибку авторизации, приложение должно очистить кэш (кроме логина и адреса) и перейти на экран логина
1.1.2 Чат
a.k.a conversationchat/беседа
Структура чата:
- Идентификатор
- идентификатор создателя
- Название чата
- Список участников
Профиль чата (редактируемые поля):
- Название чата
Приглашение в чат (invite/создать участника):
- Приглашать в чат могут только уполномоченные пользователи
- Новый участник чата не получает уведомлений, до тех пор пока не прочтет хотя бы одно сообщение
- Приглашение пользователя в чат, принудительно добавляет его, т.е отказать от приглашения невозможно
Чат -
- В чат можно попасть по приглашению, либо создав новый чат, т.е все чаты являются приватными
- Создатель чата не может покинуть свой чат
- Количество чатов у пользователя не ограничено
- Количество созданных чатов ограничено, лимит равен 100
- Управление чатом и его сообщениями опирается на permissions
- У пользователя должна быть возможность закреплять свои чаты вверху списка чатов в которых он состоит
1.1.3 Разрешения
a.k.a разрешения
Разрешения (permissions) -
- Разрешения дают доступ на какое-либо действие
- Некоторые (не все) требуют передачи параметра target, в этом случае помимо наличия права, будет выполнена проверка определенная проверка, которая где-то описана
- Набор разешений "по умолчанию" настраивается в system configuration, это те права которыми будут обладать пользователи пришедшие в чат
Пример разрешений:
- Administrator // разрешают все, а снять это разрешение нельзя - некая защита от дурака
- ManageMemberPermissions
- AddMembers
- DeleteMembers
- DeleteMemberMessages
- EditChatInfo
DeleteOwnMessageEditOwnMessage- SendMessages
1.1.4 Системные настройки
a.k.a System configuration
1.1.5 Участник чата
a.k.a member
Структура участника:
- Идентификатор
- Идентификатор пользователя
- Идентификатор чата
- permission set
Участник -
- Факт отношения пользователя к определенному чату
1.1.6 Сообщение
a.k.a message
Типы событий, в результате которых приходит анонимное сообщение в чат:
- Изменение профиля чата
- Создание нового участника
- Удаление участника
- Самостоятельный выход участника
- Создание чата
Структура сообщения:
- идентификатор
- идентификатор чата
- идентификатор пользователя, т.е автора сообщения (необязательно)
- идентификатор сообщения, на которое это сообщение отвечает
- текст (отсутствует если оно было удалено)
- дата создания
- дата редактирования (необязательно)
- дата удаления (необязательно)
- список пользователей, прочитавших сообщение (исключая создателя)
Сообщение (message) -
- Может быть создано пользователем, являющимися участником чата, либо в результате события в чате
- Текст может быть множество раз отредактирован автором, если не сообщение является удаленным
- Может быть единожды удалено автором
- На текст сообщения есть ограничение в 4096 символов
- На существующее (даже если удалено) сообщение можно отвечать другим сообщением
- Полная история сообщений в чате доступна каждому участнику
1.1.7 Push-уведомление
a.k.a notification/push-notification
Структура пуша:
- Название чата
- Текст сообщения (необязательно)
- Текст события (необязательно)
В каких случаях пользователь получает пуш:
- В любой чат в котором он состоит пришло новое сообщение
- Приглашение пользователя (только тому кого пригласили)
- Удаление пользователя (только тому кого удалили)
Когда пользователь не получает уведомления о новых сообщениях:
- Если его только пригласили в чат (является новым участником), и он ни одного сообщения не прочел
- Если пользователь уже находится в этом чате, т.е он открыт на клиенте
- Если этот чат находится в муте у пользователя открывает
Уведомления -
- Уведомления создают push-уведомления на клиенте, даже если сейчас он закрыт
1.1.8 Пользователь
a.k.a user
Структура пользователя:
- Идентификатор
- Юзернейм
- Логин
Профиль пользователя (редактируемые поля):
- Юзернейм
Пользователь -
- Пользователя можно создать пройдя регистрацию
- "Вход в профиль" осуществляется по логину
- Профиль можно редактировать
1.2 Интерфейс ui/ux
1.2.1 Мобильное приложение
Вход в приложение
{width=603px}
Макет
Экран выбора сервера и ввода логина -
- Открывается при первом входе в приложение
- Выбранный сервер сохраняется в кэш
- Компоненты:
- Инпут для адреса
- Кнопка "пинг сервера"
- По нажатию производится проверка доступности сервера и совместимости версии апи
- Инпут логина
- Кнопка "вход"
- По нажатию происходит получение токена
- После открывается экран чатов
Главный экран со списком чатов -
- Компоненты:
- Кнопка "посмотреть профиль"
- Открывает диалог профиля пользователя
- Кнопка "Создать чат*
- Открывает диалог создания чата
- Список чатов
- По нажатию на чат, откроется его экран сообщений
- Отсортирован по времени сообщений, последний сверх, а также по закрепленным, закрепленные сверху
- Каждый элемент состоит из:
- Названия
- Индикатора количества новых сообщений
- Автора (если есть) и текста сообщения
- По длительному нажатию открывается диалог действий с чатом
- Кнопка "посмотреть профиль"
Диалог действий с чатом -
- Компоненты:
- Кнопка "открепить\закрепить"
- при закрепить - чат перемещается на первое место в списке закрепленных
- при открепить - из закрепленных перемещается на первое место в списке чатов
- Кнопка "покинуть"
- по нажатию запрашивается подтверждение
- после подтверждения выхода, чат удаляется из списка чатов пользователя
- Кнопка "открепить\закрепить"
Диалог создания чата -
- Компоненты:
- инпут с названием чата
- кнопка подтверждения
- по нажатию кнопка переходит в состояние загрузки
- после создания чата, он добавляется в список и открывается экран с этим чатом
Диалог профиля пользователя -
- Компоненты:
- Идентификатор
- подсказка о том что ИД нужно передать модератору чата чтобы он мог пригласить
- Юзернейм
- Логин
- подсказка о том что Логин используется для входа в профиль
- кнопка Редактировать
- открывает диалог Редактирования профиля пользователя
- Кнопка выхода
- по нажатию открывается диалог подтверждения
- после подтверждения - Выбрасывает на экран логина
Диалог Редактирования профиля пользователя -
- Компоненты:
- инпут с юзернеймом
- кнопка подтверждения
- по нажатию кнопка переходит в состояние загрузки
- после успешного редактирования, данные пользователя обновляются и диалог закрывается
Диалог профиль чата -
- Компоненты:
- юзернейм создателя,
- название чата
- Кнопка Изменить профиль чата
- открывает диалог редактирования данных профиля чата
- Кнопка покинуть чат (необязательно)
- Кнопка добавить участника
- Открывает диалог
- Компоненты
- Инпут ид пользователя
- кнопка пригласить
Диалог профиль чата -
- Компоненты:
- Список участников
- Каждый элемент состоит из:
- юзернейм
- permission set
- По нажатию на пользователя открывается диалог Участник чата (только если нажал не на себя)
- Каждый элемент состоит из:
- Список участников
Диалог участника чата -
- Компоненты:
- ид профиля
- юзернейм
- кнопка удалить (необязательно)
- открывает диалог с подтверждением удаления
- кнопка "назначит набор разрешений" (необязательно)
- открывает диалог со списков наборов и их описания/списка возможностей
- каждый элемент это radio button
Экран сообщений чата -
- Компоненты:
- Инпут бар сообщения
- Компоненты:
- Выбранное сообщение для ответа (необязательно)
- Введенные текст (необязательно)
- Кнопка снизу, варианты:
- "Применить изменения" - если происходить изменение сообщения
- По нажатию применяется изменение, текст сообщения изменяется
- Кнопка "Отправить" - если введен текст нового сообщения
- По нажатию отправляется новое сообщение, оно появляется позже в чате
- Кнопка "Начать ввод" - если сообщение пустое и клавиатура скрыта
- По нажатию открывается клава
- "Применить изменения" - если происходить изменение сообщения
- Компоненты:
- Тайтл бар
- Компоненты:
- Кнопка "вернуться"
- название чата
- Кнопка "меню"
- Открывает диалог профиль чата
- Компоненты:
- Список сообщений:
- Каждый элемент состоит из:
- Имени отправителя (необязательно)
- Части сообщения на которое был сделан ответ (необязательно)
- Текста сообщения
- Даты отправки
- Пометки "отредактировано" (необязательно)
- Пометки "удалено" (необязательно)
- По нажатию открывается меню
- Компоненты:
- Кнопка скопировать сообщение
- Кнопка ответить на сообщение
- помещает часть сообщения в Инпут бар сообщения
- Кнопки удалить и изменить сообщение (необязательно)
- По нажатию, появится диалог с подтверждением
- Компоненты:
- Каждый элемент состоит из:
- Инпут бар сообщения
диалог ошибки версии api -
- Компоненты:
- текст с руководством о том почему возникла эта ошибки и способы ее исправления
- версия апи приложения
- версия апи с сервера
1.3 Техническая часть
1.3.1 API для приложения
Эндпоинты
// Запросить информацию о сервере и сверить версию клиента
GET {host}/healthcheck
response data:
api_support {
min_code int
min string
max_code int
max string
}
// Запросить токен для дальнейшей работы
POST {host}/auth
request body:
login string
response data:
access string
// Запросить пользователя по токену
GET {host}/users
headers:
x-token-header string
response data:
user User
credentials {
login string
}
// Запросить пользователя по ID
GET {host}/users/{id}
path param:
id int
response data:
user User
// Изменить данные пользователя
PATH {host}/users
request body:
username string?
// Запросить полный список чатов в которых пользователь является участником
GET {host}/chats
response data:
chats []Chat
// Изменить данные чата
PATH {host}/chats/{id}
path param:
id int
request body:
name string?
// Список закрепленных чатов
GET {host}/chats/pins
response data:
chat_ids []int32
// Изменить список закрепленных чатов
POST {host}/chats/pins
request body:
chat_ids []int32
// Запросить полный список участников чата
GET {host}/chats/{id}/members
path param:
id int
response data:
members []User
// Удалить пользователя из чата
DELETE {host}/chats/{chat_id}/members/{id}
path param:
chat_id int32
id int32
// Добавить пользователя в чат
POST {host}/chats/{id}/members
path param:
id int
request body:
user_id int
// Назначить permissions участнику
PATH {host}/chats/{chat_id}/members/{id}
path param:
chat_id int32
id int
request body:
permissions []Permission
// Запросить часть истории сообщений в чате
GET {host}/chats/{chat_id}/messages
path param:
chat_id int32
query param:
limit int
oneOf(
around_id int32
before_id int32
after_id int32
)
response data:
messages []Message
// Удалить сообщение
DELETE {host}/messages/{id}
path param:
id int
// Изменить сообщение
PATH {host}/chats/{chat_id}/messages/{id}
path param:
chat_id int32
id int
request body:
text string?
// Cоздать сообщение
POST {host}/chats/{chat_id}/messages
path param:
chat_id int32
request body:
text string
reply_id int32?
// Пометить сообщение как прочитанное
POST {host}/chats/{chat_id}/messages/{id}/read
path param:
chat_id int32
id int32
Модели
User:
id int
username string
Permission:
name string
desc string
PermissionId:
| Administrator
| ManageMemberPermissions
| AddMembers
| DeleteMembers
| DeleteMemberMessages
| EditChatInfo
| SendMessages
ReplyedMessage:
id int32
date int64
text string?
user User?
edit_date int64?
delete_date int64?
Message:
id int32
chat_id int32
date int64
text string?
user User?
reply ReplyedMessage?
edit_date int64?
delete_date int64?
Chat:
id int32
name string
last_msg Message?
last_read_msg int32?
unread_count int
permissions []PermissionId
Ответ с данными
// код 2**
response:
{
"data": <Данные>,
}
Ответ с ошибкой
// код 4** или 5**
response:
{
"error": "<Текст ошибки>",
}
1.3.2 Авторизация
- Administrator
- Можно выполнять любые действия
- Запрещено выдавать/забирать разрешение Administrator
- ManageMemberPermissions
- Устанавливать разрешения участникам
- Список разрешений ограничен сверху максимальным разрешением пользователя (не включая его в список)
- Применять в отношение пользователей чьи разрешения ниже уровня ManageMemberPermissions
- DeleteMembers
- Удалять (кикать\исключать) участников
- Применять в отношение пользователей чьи разрешения ниже уровня DeleteMembers
- AddMembers
- Добавлять пользователей в чат
- DeleteMemberMessages
- Удалять сообщения участников
- Применять в отношение пользователей чьи разрешения ниже уровня DeleteMemberMessages
- EditChatInfo
- Редактировать данные чата
DeleteOwnMessageУдалять свои сообщения
EditOwnMessageРедактировать свои сообщения
- SendMessages
- Писать сообщения
1.3.3 Аутентификация
- хранимые данных в jwt
- ид пользователя
- время жизни токена
- 15 минут
- способ получения токена
- в обмен на креды (логин)
- способ обновления токена
- отсутствует
1.3.4 Пользовательская БД. Субд. Схема Бд
1.3.5 Логирование
1.3.7 Тестирование
1.3.8 Gateway/Балансировщик
1.3.8 Очередь
1.3.9 Деплой
1.3.10 Версионирование
2. Сбор требований
2.1 Технические вопросы
- На чем основана аутентификация? jwt или другие варианты? Если jwt то какая нагрузка у токенов будет?
- Один инстанс сервера будет использоваться для всех чатов, т.е. 1:n либо 1:1, если таковые будут.
- Будет ли поддержка горизонтального масштабирования? Если да, то что будет шлюзом?
- Какой api будет между сервером, http, grpc, gql?
- Будет ли использоваться долгоживущее соединение - websocket, long polling, etc?
- Сервер будет stateless или statefull?
- Будет ли сервер писать логи, как их можно будет читать?
- Что будет использоваться для хранения данных - сообщения, данные чатов, пользователей?
Сообщения
- Как будет происходить пагинация при запросе сообщений?
2.2 Анализ требуемого функционала
- Какой клиент будет у "Cute-chat", web, mobile, desktop?
- "Cute-chat" это о одном чате, т.е. все сообщения будут сваливаться в одну кучу или будет возможность отправлять сообщение в разные чаты?
Несколько чатов
- Если чатов несколько, то вероятно их кто то должен создавать, кто это будет делать, или у кого такая возможность будет?
- Как пользователи будут попадать в чаты? По приглашению, присоединяться самостоятельно, присоединяться принудительно?
- Ограничения на вход будут? Т.е. приватые/публичные чаты?
- Если возможность создавать чаты будет у пользователя, то вероятно должны быть ограничения на количество созданных чатов?
- Как выглядит профиль чата? В профиле будут название, картинка, описание, список участников?
- Будут ли у создателя чата особые права? Например он может изменять профиль чата, удалять чат, добавлять\исключать участников, модерировать?
- Может ли создатель чата выходить из чата? Что будет если создатель выйдет из чата?
- Будет возможность передавать права создателя?
- Возможность удалить чат будет? Что произойдет с участниками? Если в этот момент пользователь перейдет по приглашению в чат, что он увидит?
- Есть ли возможность просматривать список участников чата? Кому эта возможность доступна?
- В чате есть ограничения по количеству участников?
- Можно ли пересылать сообщения между чатами?
Сообщения
- Что в чат можно отправлять пользователям? Медиа/стикеры/ссылки/текст/файлы?
- Сообщение может содержать несколько типов контента? Будет ли применяться разбиение на несколько сообщений при превышении лимита медиа или символов в тексте?
- Есть ли ограничения на отправку сообщений?
- Какие действия можно совершать с сообщениями? Удалять/изменять/пересылать/отвечать/копировать/отправлять повторно?
- Как будет выглядеть пересланное сообщение если его оригинал будет изменен/удален?
- Сохраняется ли история сообщений? Новые участники видят всю историю или только ее часть?
- Какого типа сообщения в чате будут появляться? Пользовательские/системные?
- Возможность отправлять анонимные сообщения в чат будет?
- Возможность отправлять сообщения со спойлером будет?
- Возможность отправлять сообщения с автоматическим удалением будет? Что произойдет если это сообщение будет переслано?
- Сообщения будут обладать статусом "прочитано"?
- Какой порядок сообщений в истории, последние помещаются снизу или сверху?
- Как выглядит профиль сообщения? В профиле сообщения будут дата создания, флаг о том что его прочитали, дата изменения/удаления, автор сообщения, чат?
- Поиск по сообщениям в конкретном чате будет? К поиску будет возможность добавлять фильтры, автор, даты, статус, тип (медиа\текст)?
- В текстовых сообщениях можно будет упоминать пользователей?
Новое сообщение
- Новое сообщение будет сопровождаться индикатором на чате, меткой "новое" в истории?
- Новое сообщение будет рассылать push уведомления всем участникам? Возможность отключать push уведомления будет?
Пользователь
- Откуда берется пользователь? Он создается исключительно для "cute-chat", т.е. проходит регистрацию или база пользователей уже существует?
- Как выглядит профиль пользователя? Имя, почта, ид, фото, описание, список чатов? Какие данные пользователь может редактировать?
- Будут ли существовать пользователи с дополнительными возможностями? Например с возможностью редактировать/просматривать/удалять то что обычные не могут
Клиент web/mobile/desktop
- Как будет происходить связывание клиента и сервера, в клиенте будет статически прописан адрес или пользователям надо вручную указывать адрес сервера?
- Будет ли ограничения при использовании клиента не последней версии либо отчающейс я от поддерживаемой сервером?
Дополнение
Проект получил другое название: cute chat -> nice pea chat
Сокращения:
nice pea chat - нпс / проект / npc
permission set - набор прав / пермишны / ps
chat - чат / беседа
Авторизация:
Что было придумано
Планировалось реализовать авторизацию с помощью PS, которые должно были работать примерно так:
Клиент отправляет запрос - хочет выполнить действия X, оно выполнится если один из его PS разрешает его.
Т.е. PS это роль, которая дает право пользователь на выполнение определенных действий.
Зачем нужна авторизация нам
В первую очередь - модерация чатов
Гибкость для создания своих "миров" как в дискорде
Посмотрим какие подходы существуют
RBAC - Управление доступом на основе ролей
Данный подход предполагает использование ролевой системы доступа, где права пользователей определены на сновании должности, уровня полномочий, ответственности, потребностей согласно обозначенным задачам
ABAC - управление доступом на основе атрибутов
Более расширенный подход, где в качестве разрешения доступа выступает использование определенных атрибутов. Это могут быть атрибуты местоположения пользователя, например определённый регион. Это могут быть атрибуты времени, когда доступ разрешён в определённые часы, дни или на определённый период.
Новый вариант авторизации
Пользователи будут составлять правила, используя строительные блоки:
- action / действие
- название
- subject
- role / роль
- id
- название (оно и определяет приоритет)
- priority / приоритет будет определяться сортировкой
Роли как в дискорде!
Пусть этот функционал будет называться "пермишенами"!
В БД отразим авторизацию так:
role ( id int name string permissions []int )
enum Permission ( id int name string # <-- наверное будет локализация, так что без этого поля обойдемся )
А ограничения будут отражаться в коде.