Перейти к содержанию

Организация структуры приложения

Хранить код Node.js приложения в пределах одного скрипта и одновременно поддерживать в нем порядок весьма затруднительно. Рассмотрим, как можно грамотно организовать структуру проекта.

Предположим, что имеется приложение, предоставляющее API для оформления заказов на зарегистрированных пользователей, со следующей структурой файлов.

Структура приложения

Теперь рассмотрим пример с описанной выше структурой.

app.js

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
const express = require('express'),
    app = express(),
    routes = require('./routes/index');

const host = '127.0.0.1';
const port = 7000;

app.use(express.json());
app.use(express.urlencoded({ extended: true }));

app.use('/api', routes);

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

routes/index.js

1
2
3
4
5
6
7
const express = require('express'),
    router = express.Router(),
    usersRoutes = require('./users.routes');

router.use('/users', usersRoutes);

module.exports = router;

routes/users.routes.js

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const express = require('express'),
    router = express.Router(),
    UserController = require('../controllers/users.controller'),
    UsersService = require('../services/users.service');

router.use(async (req, res, next) => {
    let data = await UsersService.getUsers();

    if (data) {
        req.users = data;
        next();
    } else
        return res
            .status(500)
            .send({ message: 'Error while getting users' });
});

router
    .route('/')
    .get(UserController.getUsers)
    .post(UserController.createUser)
    .put(UserController.updateUser)
    .delete(UserController.deleteUser);

module.exports = router;

controllers/users.controller.js

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
const UsersService = require('../services/users.service');

class UsersController {
    getUsers(req, res) {
        if (req.query.id) {
            if (req.users.hasOwnProperty(req.query.id))
                return res.status(200).send({
                    data: req.users[req.query.id],
                });
            else
                return res
                    .status(404)
                    .send({ message: 'User not found.' });
        } else if (!req.users)
            return res
                .status(404)
                .send({ message: 'Users not found.' });

        return res.status(200).send({ data: req.users });
    }

    async createUser(req, res) {
        if (req.body.user && req.body.user.id) {
            if (req.users.hasOwnProperty(req.body.user.id))
                return res.status(409).send({
                    message: 'User already exists.',
                });

            req.users[req.body.user.id] = req.body.user;

            let result = await UsersService.createUser(
                req.users
            );

            if (result) return res.status(200).send(result);
            else
                return res.status(500).send({
                    message: 'Unable create user.',
                });
        } else
            return res
                .status(400)
                .send({ message: 'Bad request.' });
    }

    async updateUser(req, res) {
        if (req.body.user && req.body.user.id) {
            if (!req.users.hasOwnProperty(req.body.user.id))
                return res
                    .status(404)
                    .send({ message: 'User not found.' });

            req.users[req.body.user.id] = req.body.user;

            let result = await UsersService.updateUser(
                req.users
            );

            if (result) return res.status(200).send(result);
            else
                return res.status(500).send({
                    message: 'Unable update user.',
                });
        } else
            return res
                .status(400)
                .send({ message: 'Bad request.' });
    }

    async deleteUser(req, res) {
        if (req.query.id) {
            if (req.users.hasOwnProperty(req.query.id)) {
                delete req.users[req.query.id];

                let result = await UsersService.deleteUser(
                    req.users
                );

                if (result)
                    return res.status(200).send(result);
                else
                    return res.status(500).send({
                        message: 'Unable delete user.',
                    });
            } else
                return res
                    .status(404)
                    .send({ message: 'User not found.' });
        } else
            return res
                .status(400)
                .send({ message: 'Bad request.' });
    }
}

module.exports = new UsersController();

services/users.service.js

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
const fs = require('fs');

class UsersService {
    getUsers() {
        return new Promise((res, rej) => {
            fs.readFile('data.json', (err, data) => {
                if (err) {
                    return res(false);
                }
                return res(JSON.parse(data));
            });
        });
    }

    createUser(data) {
        return new Promise((res, rej) => {
            fs.writeFile(
                'data.json',
                JSON.stringify(data),
                (err, response) => {
                    if (err) return res(false);

                    return res({
                        message: 'User created.',
                    });
                }
            );
        });
    }

    updateUser(data) {
        return new Promise((res, rej) => {
            fs.writeFile(
                'data.json',
                JSON.stringify(data),
                (err, response) => {
                    if (err) return res(false);

                    return res({
                        message: 'User updated.',
                    });
                }
            );
        });
    }

    deleteUser(data) {
        return new Promise((res, rej) => {
            fs.writeFile(
                'data.json',
                JSON.stringify(data),
                (err, response) => {
                    if (err) return res(false);

                    return res({
                        message: 'User deleted.',
                    });
                }
            );
        });
    }
}

module.exports = new UsersService();

В файле app.js содержатся только основные функции промежуточной обработки и функция запуска вашего Node.js приложения.

В директории config находятся файлы для каждой возможной среды окружения, в которой может быть запущено приложение. В такие файлы выносится конфигурация приложения, которая меняется в зависимости от среды запуска.

Директория controllers хранит классы, методы которого выступают обработчиками маршрутов. При этом контроллер создается для каждой взаимосвязанной совокупности маршрутов, например, для всех маршрутов, отвечающих за выполнение действий над заказами.

В services также находятся классы, но их методы отвечают за поставку данных контроллерам, например, с их помощью происходит обращение к базе данных или стороннему API.

В директории routes находятся описания всех маршрутов, причем для любая взаимосвязанная совокупность маршрутов выносится в отдельный файл. Также в routes присутствует файл index.js, который служит для структурированного построения маршрутов. Так, например, с его помощью можно задать префикс маршрутам отдельной группы.

В идеальном варианте у вас всегда должны присутствовать файлы в каждой из директорий controllers, services и routes. Один файл должен описывать самостоятельную логическую часть Node.js приложения.