Как в базе данных указать несколько ролей у одного пользователя?

«Как в базе данных указать несколько ролей у одного пользователя?» — вопрос из категории Базы данных, который задают на 26% собеседований Node.js Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

В Node.js-приложениях выбор структуры зависит от типа базы данных. Вот основные подходы:

1. Для реляционных БД (PostgreSQL, MySQL) — Связующая таблица (Many-to-Many): Это наиболее нормализованный и гибкий способ.

-- Таблицы
CREATE TABLE users (id SERIAL PRIMARY KEY, email VARCHAR(255));
CREATE TABLE roles (id SERIAL PRIMARY KEY, name VARCHAR(50));
-- Связующая таблица
CREATE TABLE user_roles (
  user_id INT REFERENCES users(id) ON DELETE CASCADE,
  role_id INT REFERENCES roles(id) ON DELETE CASCADE,
  PRIMARY KEY (user_id, role_id)
);

Работа через Sequelize (ORM):

// Модели с ассоциацией Many-to-Many
User.belongsToMany(Role, { through: 'UserRole' });
Role.belongsToMany(User, { through: 'UserRole' });

// Назначение ролей пользователю
const user = await User.findByPk(1);
const adminRole = await Role.findOne({ where: { name: 'admin' } });
await user.addRole(adminRole);

// Получение пользователя с его ролями
const userWithRoles = await User.findByPk(1, {
  include: Role
});

2. Для документ-ориентированных БД (MongoDB) — Массив в документе: Простой и быстрый способ для случаев, когда набор ролей ограничен и не требует сложных запросов.

// Схема Mongoose
const userSchema = new mongoose.Schema({
  email: String,
  roles: [{
    type: String,
    enum: ['user', 'editor', 'admin', 'moderator'] // Валидация
  }]
});

// Добавление роли
await User.updateOne(
  { _id: userId },
  { $addToSet: { roles: 'editor' } } // $addToSet предотвращает дубликаты
);

3. Битовая маска (для простых, статичных наборов прав): Эффективно по памяти, но менее гибко и читаемо.

// Определение ролей как степеней двойки
const ROLES = {
  USER: 1 << 0, // 1
  EDITOR: 1 << 1, // 2
  ADMIN: 1 << 2, // 4
  MODERATOR: 1 << 3 // 8
};

// Пользователь - редактор и модератор
user.permissions = ROLES.EDITOR | ROLES.MODERATOR; // 10 (2 + 8)

// Проверка роли
const isEditor = (user.permissions & ROLES.EDITOR) === ROLES.EDITOR;

Выбор подхода:

  • Связующая таблица — для сложных систем с динамическим управлением ролями, ACL (Access Control Lists).
  • Массив в документе — для микросервисов или приложений, где данные пользователя часто читаются целиком.
  • Битовая маска — для высоконагруженных систем с простой, неизменной логикой проверки прав.