Несколько объектов, совместно использующих одну и ту же таблицу отношений ролей

#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 может сделать это за вас?