Создание абстракций
Цель: переписать код сервера разделив логику приложения и инфраструктурный код, введя абстракции, инкапсулирующие реализацию в замыкания функций, модулей и классах.
Подготовка
Изучить объявление классов, создание экземпляров классов, использование функции конструктора, публичные и приватные поля и методы. Повторить замыкания для функций и модулей.
В этом задании упор делается на организацию и удобство в работе с кодом и упрощением добавления новых путей и методов, с которыми может работать сервер. Для этого потребуется создать классы для приложения и шаблонизатора.
Многоуровневая архитектура
Многоуровневая архитектура или N-уровневая архитектура. Эта архитектура организует приложение на логические уровни, каждый из которых несет определенную ответственность:
- Уровень контроллера: принимает запросы от пользователей и взаимодействует с уровнем сервиса.
- Уровень сервиса: выполняет операции бизнес-логики и взаимодействует с DAL для доступа к данным.
- Уровень доступа к данным (DAL): обрабатывает CRUD-операции с базой данных.
Эта структура помогает разделить задачи и упрощает обслуживание и масштабирование приложения.
Поток в трехуровневой архитектуре
- Клиент отправляет запрос.
- Соответствующий контроллер получает запрос на основе URL-адреса и метода.
- Контроллер вызывает сервис для обработки входящего запроса. В сервис передается необходимый минимум информации.
- Сервис запрашивает данные из хранилища.
- Сервис обрабатывает данные, извлеченные из хранилища, и возвращает их контроллеру.
- Контроллер передает ответ клиенту.
Источники
- learn.javascript.ru: Классы
- MDN: Классы
- learn.javascript.ru: Замыкания
- Методы рефакторинга
- Node.js project architecture best practices
Практика
Класс приложения
- В отдельном файле объявите класс
VanillaApp
, который инкапсулирует логику настройки и работы сервера. - Используйте встроенный в Node.js модуль
http
для создания HTTP-сервера. - Реализуйте метод
add
в классеVanillaApp
, который позволяет добавлять новые url и методы HTTP с контроллерами - функции работающие с request, response. - Реализуйте метод
listen
, который запускает сервер на указанном порту и хосте. - Реализуйте метод
static
, который позволяет возвращать статические файлы из папки в качестве ответа. - Рассмотрите возможность использования объекта или map для хранения обработчиков маршрутов (url + метод) и соответствующих им функций.
Шаблонизатор
- В отдельном модуле объявите класс
TemplateEngine
, который инкапсулирует логику шаблонизации: санитизации html, подстановки значения. - Создайте исходный набор правил для санитизации и символы для поиска шаблона, но дайте возможность их менять.
- Реализуйте метод
compile
, который принимает строку, а возвращает функцию, которая принимает объект и выполняет подстановку в шаблон.
Правила замены можно задавать в виде массива, например [[/&/g, "&"], [/</g, "<"]]
.
Контроллеры
Далее код нашего приложения будет разделен на слои. Контроллеры - это первый слой. Задача контроллера (функции) заключается в работе с объектами запроса и ответа, то есть он должен в итоге подготовить корректный ответ для клиента и он вызывает сервисы для получения обработанной информации.
Функции, которые вы передаете в качестве аргумента в метод add, теперь должны храниться в модулях в папке controllers
и быть импортированы в основной файл приложения.
Сами контроллеры при вызове должны получать в качестве аргументов объекты запроса и ответа.
Сервисы
В функции сервисного слоя передается необходимый минимум информации от контроллера, далее он выполняет вычисления и возвращает данные контроллеру для подготовки ответа.
В вашем коде логика реализована, ваша задача только корректно ее разделить.
Примерная структура проекта
project/
├── controllers/
│ ├── usersController.js
│ └── ...
├── services/
│ ├── userService.js
│ └── ...
├── app.js
└── package.json
Пример файла app.js
import { Server } from './server.js'
import { TemplateEngine } from './templateEngine.js'
import { rootGetController, rootPostController } from './controllers/rootControllers.js'
import { getAllComments } from './services/commentService.js'
const PORT = 5500
const HOST = '127.0.0.1'
const FILES = { ...}
const myServer = new Server()
myServer.static(FILES)
myServer.add('GET', '/', rootGetController)
myServer.add('POST', '/', rootPostController)
const template = TemplateEngine.compile(template)
myServer.add('GET', '/comment', (req, res) => {
const data = template(getAllComments())
res.setHeader('Content-Length', Buffer.byteLength(data))
res.writeHead(200)
res.end(data)
})
myServer.listen(PORT, HOST, () => {
console.log(`Server is running on http://${HOST}:${PORT}`);
})
Тестирование
После рефакторинга должен работать весь функционал из предыдущей работы. Тесты для сервера у вас уже написаны, используйте их для проверки работоспособности кода.
Также проведите нагрузочное тестирование, чтобы убедиться что нет радикальной деградации скорости работы сервера.
Требования к коду и результат выполнения
- Код отформатирован, не содержит ошибок и замечаний от статического анализа кода ESLint, сохранен в системе контроля версий.
- Отсутствуют ошибки в консоли.
- Работающий локально сервер с логикой, которая соответствует требованиям задания.
- Сервер проходит все тесты.
Вопросы для защиты (3 вопроса по 10 баллов)
- Рефакторинг и примеры его методов.
- Классы в JS. Объявление, создание экземпляра.
- Приватные и публичные поля и методы. Назначение и создание в JS.
- Статические методы класса.
- Объект this и его работа в классе.
- Слоистая архитектура (Layered Architecture). Слои контроллеров и сервисов.