Привет!
Столкнулся с противоречием при реализации сущностей в рамках DDD. С одной стороны мы должны следить за тем чтобы не было возможности получить сущность в невалидном состоянии, а с другой показать в пользовательском интерфейсе какие значения неправильные и почему
На самом деле с точки зрения DDD как раз-то противоречий никаких нет. Модель предметной области существует сама по себе, и не делает никаких предположений о своих клиентах, которыми могут быть графические интерфейсы настольных и веб-приложений, и не только...
Как быть если ошибка в нескольких атрибутах сущности?
Такого не может быть по той причине, что бизнес-сущность защищает свои инварианты и всегда должна находиться в валидном состоянии, поэтому если вы попытаетесь установить хотя бы один атрибут неверным значением, то сразу будет выброшено исключение.
Дублировать валидацию на клиенте и сервере не хочется
Дублировать валидацию можно, если она общеизвестная, например ФИО или email-адрес, зачем перепадать эти значения на сервер, когда в браузере можно выполнить их полноценную валидацию, что повысит время отклика UI. А если логика специфичная, что чаще и бывает, то бизнес-правила должны "лежать" на уровне домена, и не выше.
В общем, мне известно два варианта создания сущностей: используя только публичный конструктор, или используя фабрику(factory). Если я вас правильно понял, то у вас имеется форма с полями для заполнения их значениями, которые являются атрибутами какой-то сущности?
Здесь могут быть два сценария, первый: когда сущность, а точнее АГРЕГАТ, извлекается из репозитория с целью редактирования, то здесь все так, как вы описали выше: неверное значение атрибута - выбрасываем исключение.
И второй сценарий - создание сущности. Нигде нельзя получить доступ к невалидной сущности, созданной с помощью оператора
new DomainObject()
кроме как фабрики. Поэтому создавайте фабрику, которая позволяет строить вашу сущность, а точнее АГРЕГАТ, по шагам. Для этого примените паттерн проектирования СТРОИТЕЛЬ (
Builder). В конце вызовите метод
Build(), который и вернет вам валидный АГРЕГАТ.
Что качается промежуточных результатов "строительства", то для их получения используйте паттерн проектирования СНИМОК (
Memento), например вот так:
var snapshot = domainObjectFactory.GetSnapshot();
, где snapshot обычная DTO-ошка, которая содержит значения атрибутов сущностей и списки ошибок, валидность и т.д. Ну а дальше смотрите сами как быть, если вы на клиента передаете ViewModel-s, то используя snapshot создавайте модели представления и дальше их на UI пользователя.