Реализация доменного дизайна: зачем включать TenantId во все запросы репозитория? - PullRequest
1 голос
/ 20 апреля 2020

Я пытаюсь понять, почему Вон Вернон (в примере кода Github для красной книги) включает tenantId в каждый метод получения или поиска в репозитории. В частности, те, которые выполняют базовый c getById.

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

class UserRepository:
    public User userWithUsername(TenantId aTenantId, String aUsername)

Совершенно логично.

Но в других своих BC он использует объект значения идентификатора, основанный на GUID, но все же комбинирует его с tenantId при извлечении из хранилища:

class ProductRepository:
    public Product productOfId(TenantId aTenantId, ProductId aProductId)

Даже в примере CQRS используется комбинация tenantId и entityId для одного метода get для репозиториев.

Повсеместно, но я не могу понять, зачем это нужно. Если продукт, элемент журнала, форум, календарь и т. Д. c имеют глобально уникальный идентификатор, зачем вам что-то еще, чтобы запросить их?

Если использовать это, чтобы гарантировать, что только объекты для конкретного арендатора могут быть найдено - это не ответственность, ожидаемая от хранилища, а скорее аутентификация et c, и поэтому зачем включать в каждый метод запроса хранилища?

Обратите внимание, я понимаю, почему в качестве атрибута используется tenantId от сущности требуется, и как это может быть использовано для обеспечения некоторых уникальных ограничений. Однако при извлечении продукта не будет ли достаточно productOfId (ProductId aProductId)?

Он затрагивает этот вопрос: «В случае многопользовательской среды экземпляр TenantId также считается частью уникальной идентификации». Какое значение имеет объединение двух идентификаторов GUID для определения идентичности над одним?

1 Ответ

2 голосов
/ 20 апреля 2020

Не думаю, что это конкретно от c до DDD, но для многопользовательской. Независимо от того, как вы разрабатываете свое приложение, вы должны иметь возможность определить, принадлежит ли запись или организация арендатору или другому лицу. Таким образом, даже если идентификаторы глобально уникальны, поэтому для дедупликации не требуется другая часть идентификатора, вам все равно нужен идентификатор арендатора.

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

api/{tenantId}/entity-type/{entityId}

Вам нужно будет подтвердить, что пользователь, выполняющий запрос, имеет доступ к указанному tenantId на основе, например, утверждений в токен аутентификации. Если у пользователя есть доступ, то вы будете читать из базы данных. Но если ваш репозиторий принимает только «entityId», он вернет эту сущность независимо от того, к какому арендатору он принадлежит, и пользователь из tenant1 может получить данные от любого другого арендатора, только зная Id. Конечно, вы можете добавить проверку идентификатора клиента после загрузки объекта, но рано или поздно вы забудете добавить его. Если вместо этого вы следуете практике добавления tenantId в свои репозитории, то эта проверка встроена в сами запросы, делая весь процесс более согласованным и эффективным.

В дополнение к этому есть другие способы добавить проверку идентификатора клиента для всех ваших запросов, что позволит достичь той же цели без необходимости вручную передавать ее для каждого вызова метода репозитория и реализации запроса. Например, вы можете поместить tenantId в контекст и внедрить его, используя DI для разрешения вашего репозитория. С сервером SQL вы можете использовать защиту на уровне строк, чтобы проверка tenantId была частью политики таблицы, а не добавляла ее ко всем запросам в этой таблице.

...