- Настройка окружения и установка необходмых пакетов для разработки (Linux, MacOS)
- Разработка
- Контейнеризация
- Проверки pre-commit
- CI
- Архитектура
- Тестирование
- API
Серверная часть будет писаться на TS. Чтобы вести разработку необходимо уставноить nvm и node.
wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.34.0/install.sh | bash
nvm install 10
nvm alias default 10
Также нужно установить утилиту yarn: https://yarnpkg.com/
После этого можно устанвливать пакеты зависимотей для данного проекта, выполнив команду:
yarn
В проекте используются токены и пароли сторонних сервисов, чтобы получить доступ к ним нужно будет прислать разработчику свои сгенерированный gpg-ключ (публичный) в виде файла. Подробнее как его сгенировать можно почитать здесь: https://help.github.com/en/github/authenticating-to-github/generating-a-new-gpg-key
Если на предыдущих шагах все прошло успешно, можно начинать разработку. Для этого нужно запустить следующую команду:
yarn start-dev
Она запустит сервер в режими development и любые изменения в коде сервера автоматически будут приводить к его перезапуску. В консоле появится информация о хосте, на котором запущен сервер. С другими командами можно ознакомиться в package.json.
Типичный workflow разработки:
- Отводится ветка от master _<short_task_description>
- Пишутся необходимые сервисы в директории
services - Заводится, если нужно ручки в
api - После написания функционала - пишутся тесты (директория
tests) - Далее пишется документация на API в отдельных файлах
api/**/*.docs.ts
Чтобы запустить приложение в docker контейнере нужно сделать следующие:
docker build . -t <name of image>
docker run -p 5050:5050 <name of image>
Можно запустить так же образ который находится в удаленном репозитории (см ниже). Пример запуска последней стабильной версии:
docker run -p 8000:5050 networksidea/backend:latest
Важно указать правильную проброску портов!: -p <port on your host>:5050, важно что в контейнере сервер слушает порт 5050!
Контейнер запускает сервер в режиме production.
Также к git-репозиторию привязан docker hub oraganisation: https://hub.docker.com/orgs/networksidea.
Нужно подать заявку для членства в организации, чтобы иметь доступ к push своих свобственных кастомных образов.
Сейчас образы собираются по следующим правилам автоматически (реагируют на гитовые web хуки):
- На каждое обновление
master, при этом образ собирается из последнего коммита и доступен под тегомlatest:
docker pull networksidea/backend:latest
- На каждый релизный тэг в мастер:
tag=0.10.1=> образ можно получить под тегом:release-0.10.1:
docker pull networksidea/backend:release-0.10.1
- На каждый push в произвольную ветку => соответсвующий тэг будет иметь имя ветки
docker pull networksidea/backend:<branch-name>
Если хочется запушить свой собственный docker образ (например он нужен другим разработчикам для интеграции), то запушить свой образ можно так:
docker push networksidea/backend:<tagname>
tagname не должен быть: latest, <branch-name>, release-<...> в силу правил описанных выше.
Соответственно другому разработчику можно получить образ выполнив команду:
docker pull networksidea/backend:<tagname>
Перед тем как сделать коммит прогонится линтер: yarn lint. В случае нарушения Code Style или соверешния ошибок, видимых при статической обработке кода, коммит не удасться сделать. В этом случае необходимо поправить код в тех местах, где ошибки и повторить попытку коммита.
Процесс CI проверко представляет из себя следующее:
- Lint checking: Eslint проверки (статическая проверка линеторм нв code-style + мелкие синтаксические ошибки)
- TS checking: проверка на типизацию TypeScript
- Server tests: серверные тесты на API
Взял за основу эту статью: https://dev.to/santypk4/bulletproof-node-js-project-architecture-4epf Выделю основные моменты из нее. Структура проекта:
- app.js # входная точка проекта
- /api # папка с контроллерами
- /config # папка с конфигами и определением переменных окружения (секретами)
- /loaders # подключение всех необходмых модулей и middlewares на этапе старта приложения
- /models # databse models
- /services # описание классов со всей бизнесс-логикой
В каждой из директорий может также распологаться папка с тестами: tests. Точно будем тестировать всю бизес логику. Поэтому после написания очередной бизнес сущности необходимо написать тесты на нее.
В архитектуре будет присутствовать 3 слоя:
- Слой контроллеров
Controllers- верхний слой. Принимает заброс и вызывает соответствующую обработку бизнесс логики из/services. Это верхний слой приложения, он принимает непосредственно запросы. В этом слое не должно быть ни в коем случае описания любой бизнесс логики. Этот слой просто принимает запрос и решает какая функция/класс будет обрабатываеть его из слоя 2. Пример:
route.post('/',
validators.userSignup, // this middleware take care of validation
async (req, res, next) => {
// The actual responsability of the route layer.
const userDTO = req.body;
// Call to service layer.
// Abstraction on how to access the data layer and the business logic.
const { user, company } = await UserService.Signup(userDTO);
// Return a response to client.
return res.json({ user, company });
});
- Слой с описанием бизнес логики приложения:
Services- средний слой. Тут описание каких-то классов (бизнес сущностей), которые непосредественно занимаются уже бизнес логикой приложения. Пример классUser(очень упрещенный без тайпингов):
import UserModel from '../models/user';
import CompanyModel from '../models/company';
export default class UserService {
async Signup(user) {
const userRecord = await UserModel.create(user);
const companyRecord = await CompanyModel.create(userRecord); // needs userRecord to have the database id
const salaryRecord = await SalaryModel.create(userRecord, companyRecord); // depends on user and company to be created
...whatever
await EmailService.startSignupSequence(userRecord)
...do more stuff
return { user: userRecord, company: companyRecord };
}
}
- Слой с описанием работы с базой данной:
Data Acess Layer. Это самый низкий слой, он занимается взаимодействием с базой данной. База данных будетMongoDB: https://www.mongodb.com/. Фреймворк для взаимодействия будет использоваться mongoose.
Важные замечения: если класс из service слоя использует какие-то модели из слоя data acess layer. То нужно вводить dependency injection (для дальнейшего тестировния). Пользуемся этой библиотекой для этого typeDI.
За эталон берем архитектуру из этого репозитория: https://github.com/santiq/bulletproof-nodejs
Процесс тестирования API устроен следующим образом: все тесты находятся в директории test и представляют из себя тестирование уже существующих написанных ручек бэкенда. Перед запуском этих тестов (а также перед запуском бэкенда в режиме development) в БД наливаются fixtures (фиктивные наборы данных). Сами фикстуры находятся в папке fixtures. БД в режиме development, а также в режиме testing поднимается локально из-под docker-контейнера.
При написании тестов можно добавлять нужные фикстуры, а также если они нужны в режиме разработки. Все тесты следует писать независимыми друг от друга (это значит что результат выполнения тестов не зависит от последовательности выполнения этих тестов, а также в случае их произвольного распараллеливания).
Ссылка на документацию по API: https://web-networks.github.io/backend/index.html с описанием работы всех ручек.