Трехуровневая архитектура — это шаблон проектирования, который разделяет приложения на веб-уровни (клиентские уровни), уровни приложений и уровни базы данных. Каждый уровень несет определенную ответственность и взаимодействует с другими уровнями через четко определенные интерфейсы. Поскольку это самый простой и популярный дизайн, в этой статье мы рассмотрим трехуровневую архитектуру с простым кодом.

Веб-слой

На веб-уровне мы обычно имеем дело с интерфейсной частью нашего приложения, которая включает в себя клиент (например, веб-браузер) и серверный код, который обрабатывает входящие запросы и отправляет ответы.

const http = require('http');

const hostname = 'localhost';
const port = 3000;

const app = http.createServer((req, res) => {
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Hello World!');
});

app.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});

В приведенном выше коде мы создаем новый HTTP-сервер, используя модуль http, встроенный в Node.js. Затем мы прослушиваем входящие запросы через порт 3000 и отвечаем на каждый запрос простым текстовым сообщением «Hello World!». Конечно, в реальном приложении вы хотели бы включить более сложную логику для обработки различных типов запросов и отправки соответствующих ответов.

Прикладной уровень

На прикладном уровне мы обычно обрабатываем бизнес-логику нашего приложения. В приложении Node.js мы можем использовать архитектуру MVC, чтобы разделить задачи нашего приложения на разные компоненты. Давайте воспользуемся фреймворком Express Web для реализации простой архитектуры MVC в Node.js.

const express = require('express');
const app = express();

// Model - MySQL Database connection
const mysql = require('mysql');
const connection = mysql.createConnection({
  host: 'localhost',
  user: 'username',
  password: 'password',
  database: 'mydb'
});

// View - using EJS template engine
app.set('view engine', 'ejs');

// Controller - using Singleton pattern
class UserController {
  static getInstance() {
    if (!UserController.instance) {
      UserController.instance = new UserController();
    }
    return UserController.instance;
  }

  getUser(req, res) {
    // get user data from database
    connection.query('SELECT * FROM users WHERE id = ?', [req.params.id], (error, results) => {
      if (error) {
        console.log(error);
        res.status(500).send('Error retrieving user data');
      } else {
        res.render('user', { user: results[0] });
      }
    });
  }
}

// Routes
const userController = UserController.getInstance();
app.get('/user/:id', userController.getUser);

// Start server
app.listen(3000, () => {
  console.log('Server started on port 3000');
});

В приведенном выше коде мы сначала создаем соединение с базой данных MySQL, используя модуль mysql. Мы также настраиваем наше приложение Express для использования механизма шаблонов EJS для рендеринга представлений.

Затем мы создаем класс UserController, который следует шаблону Singleton, что позволяет нам совместно использовать один экземпляр контроллера в приложении. В этом примере метод getUser() отвечает за извлечение пользовательских данных из базы данных и рендеринг представления с помощью EJS.

Наконец, мы определяем маршрут для URL-адреса /user/:id и сопоставляем его с методом getUser() нашего UserController. Когда делается запрос к этому URL, вызывается метод getUser() контроллера, который извлекает пользовательские данные из базы данных и визуализирует представление с помощью EJS.

Уровень базы данных

На уровне базы данных мы обычно имеем дело с взаимодействием с базой данных.

const mysql = require('mysql');

const connection = mysql.createConnection({
  host: 'localhost',
  user: 'username',
  password: 'password',
  database: 'mydb'
});

connection.connect((err) => {
  if (err) throw err;
  console.log('Connected to MySQL database!');

  const createTableQuery = `
    CREATE TABLE users (
      id INT AUTO_INCREMENT PRIMARY KEY,
      name VARCHAR(255),
      email VARCHAR(255)
    )
  `;

  connection.query(createTableQuery, (err, result) => {
    if (err) throw err;
    console.log('User table created successfully!');
  });
});

В приведенном выше коде мы создаем соединение с базой данных MySQL, используя модуль mysql. Затем мы определяем переменную createTableQuery, содержащую SQL-запрос для создания таблицы users со столбцами id, name и email.

Наконец, мы используем метод connection.query() для выполнения запроса SQL, который создает таблицу users в базе данных. Если запрос выполнен успешно, мы записываем в консоль сообщение о том, что таблица создана успешно.

Реальные приложения могут обрабатывать ошибки и реализовывать более сложные взаимодействия с базой данных. Настройка сложной логики может привести к проблемам с callback hell из-за многочисленных запросов. В качестве альтернативы можно использовать ORM. Мы объясним это более подробно в разделе ORM позже.

Заключение

До сих пор мы просто использовали Express для настройки приложения с трехуровневой архитектурой. На самом деле, если вы добавите плоти к тому, что вы сделали до сих пор, и настроите дополнительную бизнес-логику, одно приложение будет завершено.

Однако до сих пор в содержании есть много недостатков. Вам понадобится больше деталей для использования в реальных проектах.

Мы вернемся с кодом, который применяет несколько шаблонов проектирования к Express в будущем Express.