Зачем сущность Role пишут отдельно, а не добавляют роль в сущность User
Система авторизации по ролям обеспечивает управление доступом к ресурсам приложения в соответствии с ролью пользователя.
Сущность Role пишут отдельно от сущности User, вероятно, для того, чтобы разграничить роли и пользователей и упростить управление ими.
Обычно для хранения информации о пользователях и ролях создают две отдельные таблицы. Затем их связывают через промежуточную таблицу, чтобы реализовать связь «многие ко многим».
Такой подход часто используют в корпоративных системах, интернет-магазинах и других системах, где требуется гибкость в управлении пользователями.
Однако есть мнение, что если пользователь будет иметь только одну роль, то нет смысла создавать отдельную сущность для роли, а лучше поместить её в таблицу пользователя.
Нормализация базы данных (Normalization)
Плохой вариант - данные дублируются:
public class User
{
public string Role { get; set; } // "Admin", "Admin", "Admin" - дублирование
}Хороший вариант - данные в одном месте:
public class Role
{
public int Id { get; set; } // 1
public string Name { get; set; } // "Admin" - хранится 1 раз
}Целостность данных (Data Integrity)
Data Integrity — это совокупность свойств, гарантирующих точность, достоверность и надёжность информации на протяжении всего жизненного цикла данных. Это фундаментальная характеристика, определяющая степень доверия к данным в любой информационной системе.
Целостность данных — это не статичное свойство, а динамический процесс, требующий постоянного внимания. Данные могут подвергаться искажениям на любом этапе: при вводе, передаче, обработке, хранении или извлечении.
Без отдельной таблицы можно ошибиться:
user.Role = "Admn"; // Опечатка создаст новую роль
user.Role = "admin"; // Разный регистр - разные ролиС отдельной таблицей:
user.RoleId = 1; // Всегда ссылается на существующую рольГибкость и расширяемость
Можно легко добавить свойства роли:
public class Role
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public List<Permission> Permissions { get; set; } // Права доступа
public int Level { get; set; } // Уровень доступа
}Экономия места
int (4 байта) vs nvarchar(50) (~100 байт)
Для 1 млн пользователей:
int: 4 MB
string: ~100 MB + фрагментация
Производительность запросов
Индексы в базах данных повышают производительность запросов за счёт ускорения поиска данных. Это особенно важно для больших баз данных с миллионами или даже миллиардами записей.
Индексы — это дополнительная структура данных, созданная на основе столбцов таблицы. Они работают как указатель, который направляет СУБД к нужным строкам вместо того, чтобы сканировать всю таблицу.
Поиск всех админов:
SELECT * FROM Users WHERE Role = 'Admin' -- Сканирование всех строкС отдельной таблицей:
SELECT * FROM Users WHERE RoleId = 1 -- Быстрее по индексуИндекс создаёт копию определённых столбцов таблицы в виде дерева или другой структуры данных, где значения отсортированы и упорядочены. Когда приходит запрос, СУБД начинает навигацию с корневого узла, последовательно спускаясь по веткам дерева к нужным данным. Благодаря этому время поиска сокращается логарифмически — вместо просмотра тысяч записей система делает всего несколько переходов.
Безопасность
Можно контролировать изменения ролей
public class RoleService
{
public async Task UpdateRolePermissions(int roleId, List<Permission> permissions)
{
// Только админ может менять права роли
if (!_currentUser.IsAdmin)
throw new UnauthorizedException();
var role = await _context.Roles.FindAsync(roleId);
role.Permissions = permissions;
}
}Когда можно оставить роль в User?
Можно оставить строкой, если:
Маленький проект (CRUD на 5 экранов)
Роли никогда не изменятся
Нет дополнительных атрибутов у ролей
Максимум 2-3 роли и нет отдельных Permission для ролей
Для простого проекта можно и так:
public class User
{
public string Role { get; set; } // "Admin" или "User"
}Но даже так я рекомендую не писать всё в один класс, а раскидывать на несколько классов Role и User в дальнейшем писать код будет проще и не будет повторяющихся элементов.
Нужна отдельная таблица, если:
Роли могут добавляться/изменяться
У ролей есть права доступа (permissions)
Проект будет расти
Нужна история изменений ролей
Пример сложной системы ролей
Реальный пример системы с ролями и правами на примере одного из моего проектов:
public class Role
{
public int Id { get; set; }
public string Name { get; set; }
public List<Permission> Permissions { get; set; } // Что можно делать
public List<User> Users { get; set; } // Кто в роли
public bool IsDefault { get; set; } // Роль по умолчанию
public int? MaxUsers { get; set; } // Ограничение кол-ва
}
public class Permission
{
public int Id { get; set; }
public string Name { get; set; } // "CreatePost", "DeleteUser"
public string Resource { get; set; } // "Post", "User"
public string Action { get; set; } // "Create", "Read", "Update", "Delete"
}Использование:
var canDelete = user.Role.Permissions.Any(p =>
p.Resource == "User" && p.Action == "Delete");Заключение
Отдельная сущность Role — это не просто «так принято», а осознанное архитектурное решение для гибкости, безопасности и масштабируемости приложения.