Comments 39
Если у него CRUD, и сущность тупо копируется в UI а потом отредактированная копируется в репозиторий, тут нет никаких команд.
Если CQRS, логично, что валидировать надо команду, тот объект, который уходит из UI.
Ну и мелкое лукавство тоже присутствует. Почему «отображение ошибки пользователю вызывает раздражение, так как возвращается только одна ошибка за раз». Так же можно сделать валидатор сущности через RuleFor
Почему «отображение ошибки пользователю вызывает раздражение, так как возвращается только одна ошибка за раз». Так же можно сделать валидатор сущности через RuleFor
Основная мысль здесь в том, что даже если вернется несколько ошибок одновременно, непонятно, как сопоставить ошибки (привязанные к сущности) и поля интерфейса. Например, на форме могут быть раздельные поля на день, месяц и год даты рождения, а в сущности это будет единое поле типа «дата».
У нас фреймворк валидации работает следующим образом. Вся валидация находится в сущностях и частично в репозиториях. Ошибки валидации не висят в воздухе, а привязываются к ключам валидации. Для этого у нас есть класс ValidationKey с наследниками, например EntityPropertyValidationKey, который содержит в себе ссылку на сущность и название свойства, к которому он относится. В процессе сохранения данных в базу клиентский код должен связать валидационный ключ с этими данными. Если мы говорим про Web, то данные у нас приходят в полях вью-моделей. При этом данные могут перемещаться от одного объекта к другому. Например, во вью-модели мы создаём не сущность, а какую-то DTO'шку, которая в последствие передаётся в репозиторий. В такой ситуации мы связываем исходные данные с полями DTO'шки, а затем в репозитории связываем поле DTO'шки с полем сущности.
Говоря «связываем» я подразумеваю, что у нас где-то во фреймворке валидации просто хранится связь валидационных ключей между собой и валидационных ключей и путей по свойствам вью-моделей.
Надеюсь, хоть что-то понятно, просто не хотелось бы тут ещё код писать :)
Пользователь увидит то валидационное сообщение, которое возникло в сущности.
Тут у нас ещё есть заморочка с тем, что у нас разные приложения работают с одними и теми же сущностями. Например, потребитель может сохраняться через сервисы (значит, на каком-то сайте регистрируется), а может через нашу админку (значит, наш сотрудник создаёт какого-то потребителя зачем-то). И мы должны разные сообщения показывать в таком случае. Так что у нас в коде используются ключи сообщений, а сами тексты хранятся в базе, и различаются для разных приложений.
В принципе я совсем чуть-чуть рассказал в конце последней статьи (в разделе «Валидация»).
Но конечно тема очень большая, потому что требований к фреймворку валидации было довольно много. Надо собраться как-нибудь и описать это всё :)
непонятно, как сопоставить ошибки (привязанные к сущности) и поля интерфейса. Например, на форме могут быть раздельные поля
Ага, а в команде это будут отдельные поля.
В принципе понятно, хотя это тоже сомнительная идея — пилить команды под UI. Мне казалось, наоборот — вот есть команды, обеспеченные бизнес-логикой сервера, а фронтэнд должен работать с ними как есть, при необходимости разбивая дату на компоненты в UI, но не в команде.
2. Валидация в доменной модели подразумевает, что мы валидируем всю модель целиком. Если в результате транзакции оказалось, что модель неконсистентна — значит, мы просто забыли что-то проверить.
Особенно, если в операции задействовано несколько сущностей, ошибка будет касаться одной из них, а не операции в целом.
Например, если на ход не хватает маны, можно в списке выбранных заклинаний отметить как ошибочные те, которые требуют её, а прочие оставить корректными.
Сначала надо выбрать архитектуру и выбор, на что писать валидатор, станет ненужным.
Не нужно путать валидацию пользовательского ввода и невозможность выполнить команду по причинам недопустимости команды в данном состоянии сущности и/или приложения. Валидация касается только формата запроса, она не касается бизнес-логики.
Так и не понял что мешает завести типизированный экспшн, в котором будет список не прошедших валидацию полей?
Наличие логики валидации в сущности или нет — зависит от того, анемичная модель у вас или нет. Лично я совершенно ничего плохого не вижу, более того считаю другие варианты не красивыми (ну разве что отдельные Domain services).
1) экскпшены — дорогое удовольствие
2) не понятно как связать их с полями модели, грубо говоря к какой строчке формы какое сообщение прикрепить.
к какой строчке формы какое сообщение прикрепить
И опять отображение ошибки пользователю вызывает раздражение, так как возвращается только одна ошибка за раз. Я мог бы вернуть их всем скопом, но как тогда мне сопоставить их с именами полей на экране? Никак.
Почему никак? можно же простым if сделать проверку, и в случае ошибки добавить ошибку в массив ошибок
Никакотносится к сопоставлению ошибок с полями формы. Рассматривается очень простой пример, если рассматривать более интересные варианты
Например, на форме могут быть раздельные поля на день, месяц и год даты рождения, а в сущности это будет единое поле типа «дата».как тут поможет любимчик if?
Возможно придется отдельное место прописывать для ошибки. Или для каждого отдельного поля продублировать сообщение (не знаю как лучше)Попытка адаптации интерфейса под внутреннее устройство, это плохо, изменения в одном не должны особо влиять на другое.
Вы путаетесь немного. Вы говорите о вводе даты дня рождения и хотите описать его 3мя полями. 3 поля это только интерфейс для ввода одной, единой сущности — день рождения.
- Правильней использовать календарь для выбора даты;
- Если вам так хочется использовать 3 отдельных поля то вы должны свернуть их в единое целое в контроллере перед началом валидации. Обычно этим занимаются компоненты форм;
- Валидировать вам всё равно нужно конечную дату в целом, а не её части и выводить вам нужно сообщение об ошибке для всей даты.
Пример:
2017-04-31
— в апреле 30 дней;2017-02-29
— не високостный год;123-13-44
— не валидная дата впринципе. Нет смысла выводить сообщения об ошибке для каждой части даты;3000-01-01
— день рождения не может быть в будущем.
Правильней использовать календарь для выбора даты;
Очень неудобное решение в случае ввод дат сильно отличающейся от текущй (дата рождения например), если имеется в виду контрол в виде календаря, где нужно тыкать мышкой.
Согласен. Хотя есть календари которые позволяют быстро пролистывать года.
Четыре кнопки нажать обычно быстрее :) Хотя, наверное, очень многое зависит от уровня подготовки пользователя, с одной стороны, и частоты ввода с другой. Если дату надо ввести один раз, то главное наглядность и пойдет календарь, а если за рабочий день он вводит их сотни раз, то или три поля с автопереходом между ними, или вообще одно с традиционным для локали пользователя форматом, пускай даже сначала ему будет непривычно, но потом привыкнет :)
одно с традиционным для локали пользователя форматом
В этом отнашении мне нравится как сделанно поле ввода банковской карты в некоторых платежных системах. Поле ввода одно и вводишь как в одно поле, а визуально номер разбивается на 4 части.
А вот 4 отдельных поля ввода с автоматическим переходом не всегда удобны.
Как валидация команд решает следующий таск?
Имеем две формы регистрации, на страницах example.com/en/register и example.com/cn/register, на первой форме валидными считаются номера банковских карт европейских\американских банков-эмитентов, а на второй — только китайских.
В данном случае любой существующий номер карты в принципе валиден (те. команда успешно завершится), но присутствуют некоторые инварианты, введенные лишь для удобства пользователя.
Правила валидации могут быть разными. Например «Икс Зетов» валидно с точки зрения данных, но не валидно с точки зрения бизнеса, такой пользователь уже есть. Валидация формата данных может быть сделана в представлении, ограничение по символам, длине и т.п. А вот проверку валидности с точки зрения бизнеса необходимо делать в бизнес логике, при навалидной сущность модель бросает исключение. В интерфейсе модель может содержать метод для проверки валидности пользователя по конкретномому сценарию
В этом случае представление должно позаботиться о том, чтобы сделать все необходимые проверки и проинформировать пользователя. Если модель получает невалидного пользователя, она просто кидает исключение, т.к. не знает что с этим пользователем делать. Замечу, что для корректности представление должно иметь безопасный (не выбрасывающий исключение) метод проверки валидности.
>«При таком подходе моя валидация строится вокруг команд и действий, а не сущностей.»
Ни слова не говорится об области видимости: данные могут быть объявлены в интерфейсе, а каждый слой может иметь свою реализацию и оперировать «своим» типом. Но как всегда «все сильно зависит».
Автор забыл как выглядит архитектора приложения.
1) Управление попадает в контроллер\view_model\button_click
2) Контроллер вызывает метод BL
3) BL обращается к данным
На первом шаге известен контекст операции и сущностей еще нет. Поэтому обозначенные в посте проблемы — вовсе не проблемы.
Все хорошие фреймворки запускают валидацию на первом шаге, еще до того, как программист может с данными что-либо сделать. На этом уровне встроенные механизмы на основе DataAnnotations работают. Причем механизм расширяемый, можно прикрутить разные проверки.
Зачем делать команды, которые будут копировать структуру сущностей и семантику контроллеров — не ясно.
Валидация: внутри сущностей или снаружи?