#c# #asp.net-mvc #database #domain-driven-design #dns
#c# #asp.net-mvc #База данных #дизайн, управляемый доменом #dns
Вопрос:
Пытаясь придерживаться принципов DRY, будет ли следующий действительный способ заставить несколько объектов совместно использовать общую таблицу отношений ролей.
Если у нас есть следующие типы (созданные исключительно для этого примера):
class User
{
...
}
class Article
{
...
}
Для обоих этих объектов должны быть определены роли. Таким образом, роль не является уникальной ни для одного из этих объектов.
Моя идея заключалась в том, чтобы репозитории выполняли операции CRUD над этими объектами, но также позволяли Роли иметь собственный репозиторий, возможно, доступ к которому осуществляется через службу?
Репозитории будут передавать UserDTO и ArticleDTO классу Builder, который будет создавать необходимые объекты домена. В этом случае:
class User
{
...
IList<Role> Roles { get; set; }
//Other Domain objs/logic
}
class Article
{
...
IList<Role> Roles { get; set; }
//Other Domain objs/logic
}
Объект роли будет иметь таблицу ролей:
Имя идентификатора
И таблицу отношений:
ItemId RoleId ItemType
конструктор ролей / сервис, используемый для прикрепления ролей к объекту домена, возможно, мог бы выглядеть примерно так:
static class RoleBuilder
{
IEnumerable<Role> Fetch(int id, typeof(obj))
{
//fetch from RoleRep
}
bool Save(IEnumerable<Role>, int id, typeof(obj))
{
//save through role rep
}
}
Есть ли что-то изначально неправильное в этой идее? например:
public static UserBuilder
{
public User FetchUser(int id)
{
//map userDTO to user
var user = map...
//populate roles
if(user != null)
user.Roles = RoleBuilder.Fetch(id, typeof(user));
}
}
Альтернативой является то, чтобы пользователь и статья управляли своими собственными манипуляциями с ролями и, возможно, имели несколько таблиц отношений ролей, например, user_has_roles, article_has_roles
Первое решение позволило бы конечному пользователю также изменять роли (переименовывать, добавлять новые роли и т.д.) Без искажения модели домена, Тогда как во втором решении я не уверен, как это сделать чисто (обновлять их через user, через article?)
Комментарии:
1. Похоже, что для отслеживания «ролей» требуется много усилий, с которыми практически не связано поведение. Также, с технической точки зрения, было бы более уместно сказать, что и User, и Article имеют роль ‘IHaveRoles’ или ‘HasRoles’ (как это относится к конфликту именования). Если «роли» действительно являются отдельным понятием, возможно, стоит изучить, принадлежат ли они одному и тому же ограниченному контексту.
2. Спасибо Yves! Я немного подумал о структуре моего первоначального предложения и немного изменил его. Возможно, я опубликую обновление для дальнейшего использования.
Ответ №1:
Если концепция ролей отделена от User и Articles — а похоже, что так оно и есть, поскольку она применима к обоим этим объектам домена, — тогда я бы, вероятно, смоделировал is отдельно, как вы предлагаете.
Я не уверен, что вы имеете в виду, когда говорите «разрешить конечному пользователю изменять роли». Если роли представляют собой отдельную концепцию, у них, вероятно, должны быть свои собственные классы домена для представления операций, которые могут выполняться с ролями (переименование, удаление и т.д.).
Также ваш фрагмент кода, вероятно, должен выглядеть следующим образом (используя общие термины вместо недопустимого синтаксиса typeof () в приведенном выше фрагменте):
static class RoleBuilder {
static IEnumerable<Role> Fetch<T>(int id) {
switch(typeof(T)) { ... }
}
static bool Save<T>(IEnumerable<Role> roles, int id) {
switch(typeof(T)) { ... }
}
}
Я бы, вероятно, также рассмотрел возможность создания какого-то базового класса для всех объектов домена, которым могут быть назначены роли (если роли используются для безопасности, то, возможно, SecurableEntity или что-то в этом роде), тогда у вас может быть таблица базового класса в БД для поддержки общих идентификаторов для идентификаторов (чтобы вы могли избавиться от столбца ItemType в таблице ролей). В качестве альтернативы вы могли бы просто использовать GUID для идентификатора объекта, что многое упрощает.
Комментарии:
1. Привет, Дилан, «разрешить конечному пользователю изменять роли», под этим я подразумевал, что в пользовательском интерфейсе будет форма, которая позволит администраторам (или назначенным ролям) обновлять роли в системе.
Ответ №2:
Ваша идея звучит неплохо. Я бы избегал связывать роли (или, в более общем смысле, какую-либо систему авторизации) со статьями и пользовательскими моделями.
Что мне не нравится в вашем коде, так это статический RoleBuilder. Во-первых: это статично, и это приводит к связыванию. Второе: RoleBuilder не должен извлекать что-либо из репозитория, но, например, получить экземпляр somes, загруженный какой-либо службой, и построить модель ролей или просто список из этих данных. В случае, если вы разрешаете RoleBuilder запрашивать репозиторий, вы связываете ответственность разработчика с логикой запроса.
Другая проблема может возникнуть из-за вашей реализации UserBuilder. Для выборки одного пользователя это может быть нормально, но выборка нескольких пользователей с такой логикой приведет к выбору N 1.
Ответ №3:
Ваша вторая идея о нескольких таблицах отношений более уместна. Вы не сможете поддерживать чистый внешний ключ в своей базе данных с помощью первого метода. Вы должны иметь возможность обновить роль у любого объекта, и это будет отражено в следующем получении из репозитория для другого объекта. На мой взгляд, обычно лучше оставить ссылочную целостность в базе данных, и поскольку обе таблицы join имеют внешний ключ к таблице roles, это помешает вам выполнять такие действия, как удаление роли из объекта user, когда она присоединена к объекту Article. Хотя первое решение является разумным, вам придется реализовать некоторую бизнес-логику для обеспечения целостности данных… зачем загромождать ваш бизнес-уровень подобными вещами, когда ORM может сделать это за вас?