Ответ 1
Как абстракция репозитория?
Ну, я вижу несколько проблем с вашим дизайном, даже если вы защищаете свой домен от фреймворка, объявляя интерфейс IUserValidator
.
Поначалу кажется, что если это приведет к той же стратегии абстракции, что и для репозитория и других проблем инфраструктуры, но есть огромная разница по моему мнению.
При использовании repository.save(...)
вы фактически не заботитесь о реализации с точки зрения домена, потому что то, как упорствовать, не является проблемой для домена.
Тем не менее, инвариантное принудительное исполнение является проблемой для домена, и вам не нужно вникать в детали инфраструктуры (теперь UserValidtor
можно рассматривать как таковой), чтобы увидеть, из чего они состоят, и что в основном то, что вы в конечном итоге сделаете, если вы делаете этот путь, поскольку правила будут выражаться в терминах рамки и будут жить вне домена.
Зачем ему жить вне?
domain -> IUserRepository
infrastructure -> HibernateUserRepository
domain -> IUserValidator
infrastructure -> FluentUserValidator
Всегда действующие объекты
Возможно, существует более фундаментальная проблема с вашим дизайном и что вы даже не задали бы этот вопрос, если бы вы придерживались этой школы, хотя: всегда действительные сущности.
С этой точки зрения, принудительное принудительное исполнение является обязанностью самого субъекта домена и поэтому не может даже существовать без действительного. Следовательно, инвариантные правила просто выражаются в виде контрактов, и исключения возникают при их нарушении.
Поводом для этого является то, что множество ошибок возникает из-за того, что объекты находятся в состоянии, которого они никогда не должны были быть. Чтобы показать пример, который я прочитал от Грега Янга:
Предположим теперь, что a
SendUserCreationEmailService
, которое принимаетUserProfile
... как мы можем рационализировать в этой службе, чтоName
неnull
? Мы проверяем это снова? Или, скорее всего, вы просто не позаботиться о том, чтобы проверить и "надеяться на лучшее", вы надеетесь, что кто-то беспокоился для проверки его перед отправкой его вам. Конечно, используя TDD один из первые тесты, которые мы должны написать, это то, что если я отправлю клиента с anull
, чтобы оно вызывало ошибку. Но как только мы начнем писать эти виды тестов снова и снова мы понимаем... "подождите, если мы никогда не позволяло названию становиться нулевым, у нас не было бы всех этих тестов", - комментирует Грег Янг http://jeffreypalermo.com/blog/the-fallacy-of-the-always-valid-entity/
Теперь не поймите меня неправильно, очевидно, что вы не можете применять все правила проверки таким образом, поскольку некоторые правила специфичны для определенных бизнес-операций, которые запрещают этот подход (например, сохранение черновиков экземпляра объекта), но эти правила не являются которые будут применяться в каждом сценарии (например, клиент должен иметь имя).
Применяя всегда действующий принцип к вашему коду
Если мы теперь посмотрим на ваш код и попытаемся применить всегда действующий подход, мы ясно видим, что объект UserValidator
не имеет места.
UserService : IUserService
{
public void Add(User user)
{
//We couldn't even make it that far with an invalid User
new UserValidator().ValidateAndThrow(user);
userRepository.Save(user);
}
}
Поэтому в этом месте нет места для FluentValidation в домене. Если вы все еще не уверены, спросите себя, как вы интегрируете объекты ценности? Будет ли у вас UsernameValidator
проверять объект значения Username
каждый раз, когда он инициируется? Ясно, что это не имеет никакого смысла, и использование объектов ценности будет довольно сложно интегрировать с не всегда действительным подходом.
Как мы сообщаем обо всех ошибках, когда бросаются исключения?
Это то, с чем я боролся, и я просил себя некоторое время (и я все еще не совсем уверен в том, что я буду говорить).
В принципе, я понял, что работа домена не в том, чтобы собирать и возвращать ошибки, что касается пользовательского интерфейса. Если недопустимые данные доходят до домена, он просто набрасывается на вас.
Таким образом, фреймворки, такие как FluentValidation, найдут свой естественный дом в пользовательском интерфейсе и будут проверять модели представления, а не объекты домена.
Я знаю, что трудно согласиться с тем, что будет некоторый уровень дублирования, но это связано главным образом с тем, что вы, вероятно, разработчик с полным стеком, такой как я, который имеет дело с пользовательским интерфейсом и доменом, когда на самом деле это могут и должны вероятно, рассматриваются как совершенно разные проекты. Кроме того, как и модель представления и модель домена, проверка модели и проверка домена могут быть схожими, но служат другой цели.
Кроме того, если вы все еще беспокоитесь о том, чтобы быть СУХОЙ, кто-то однажды сказал мне, что повторное использование кода также "связано", и я думаю, что этот факт особенно важен здесь.
Работа с отсроченной валидацией в домене
Я не буду объяснять их здесь, но существуют различные подходы к рассмотрению отложенных валидаций в домене, таких как шаблон спецификации, и Отложенная проверка, описанный Уордом Каннингемом на языке шаблонов чеков. Если у вас есть Реализационная книга по дизайну, разработанная Вонном Верноном, вы также можете прочитать со страницы 208-215.
Это всегда вопрос компромиссов
Валидация - чрезвычайно сложный вопрос, и доказательство состоит в том, что на сегодняшний день люди до сих пор не согласны с тем, как это должно быть сделано. Есть так много факторов, но в конечном итоге то, что вы хотите, является практичным, удобным и выразительным решением. Вы не всегда можете быть пуристом и должны соглашаться с тем, что некоторые правила будут нарушены (например, вам может потребоваться утечка некоторых ненавязчивых данных о сохранении в сущности, чтобы использовать ваш ORM по выбору).
Поэтому, если вы считаете, что можете жить с тем фактом, что некоторые данные FluentValidation делают это в вашем домене и что это более практично, так что я не могу действительно сказать, принесет ли он больше вреда, чем пользы в долгосрочной но я бы этого не сделал.