Вариант использования для отображения команд / прикладного уровня: реализация
Некоторые тексты, которые я читал о DDD, указывают на то, что служба приложений или команда (CQRS) на уровне приложений тесно отражает конкретный вариант использования.
Для простых случаев использования это отображение имеет смысл, но в более сложных случаях, когда требуется многократное взаимодействие с пользователем, я пытаюсь понять, как отобразить уровень API-интерфейса, не затрагивая логику приложения, в пользовательский интерфейс.
Пример: - Представьте себе сервис приложений:
ImportProductData(date_source)
- С точки зрения System / UI, при импорте данных о продукте мы хотим проверить, существует ли какой-либо из продуктов, и если да, то предложить пользователю продолжить или нет, прежде чем продолжить.
Мой обычный подход: расширить API, чтобы включить:
DoesIncludeExistingProducts(data_source)
Если возвращает true, подскажите пользователю, хотят ли они продолжить, затем позвоните.
ImportProductData(date_source, overwrite=True)
Мой вопрос заключается в том, переносит ли это большую часть прикладной логики на уровень пользовательского интерфейса? (т.е. пользовательский интерфейс теперь контролирует, могут ли продукты быть перезаписаны, и нужно ли проверять существующие продукты перед импортом данных продукта и т. д.)
Если это так, я не могу представить, как прикладной и доменный уровни справятся с этим? Помимо:
Вызов:
ImportProductData(date_source)
Если произошел сбой, проверьте причину сбоя, а если из-за того, что продукт уже существует, предложите пользователю и снова позвоните:
ImportProductData(date_source, overwrite=True)
Это похоже на другой способ сделать то же самое, что и выше.
Это может показаться немного педантичным, но я пытаюсь предпринять согласованные усилия, чтобы сделать презентационный слой (MVC) максимально легким и тонким.
Мысли о более элегантных решениях?
2 ответа
Сначала ознакомьтесь с просветительским постом Джимми Богарда здесь https://jimmybogard.com/domain-command-patterns-validation/. Он не дает ответа на ваш вопрос, но нападает на него так, чтобы вам было легче принять решение.
В этой статье он кратко затрагивает вопрос, который, я считаю, требует большего внимания. Он рекомендует не использовать исключения в качестве механизма связи, если "пользователь часто пытается сделать что-то, что запрещено проверкой моего домена". Используя этот критерий, я предпочитаю разбивать сценарии использования, которые, как ожидается, иногда не срабатывают, на вызов проверки, сопровождаемый вызовом выполнения.
В вашем случае кажется целесообразным иметь запрос проверки (а не команду), такой как IsProductSetValidForImport. В вышеприведенной статье Джимми он пытается решить, насколько богатым должен быть возвращаемый набор ошибок, но предполагается, что возвращаемый набор из запроса будет богатым, поэтому у нас нет проблем. Вы можете вернуть реальную полностью сформированную модель представления для отображения пользователю, вместо того, чтобы пытаться втиснуть достаточно данных в строку ошибки, чтобы закрасить экран. Я предполагаю, что если 3 из 10 продуктов не смогут выполнить импорт, вы можете разрешить пользователю принудительно обновлять некоторые из них, а не другие. Это дает вам такую возможность.
Если этот запрос проверки не возвращает никаких конфликтов, дайте пользователю сообщение "Вы уверены", а затем вызовите API для выполнения команды импорта. Если какое-то существенное состояние изменилось между временем, когда вы вызвали запрос проверки, и временем, когда вы выполнили команду обновления, тогда уместно вызвать исключение и обработать последствия.
Вы не должны обращаться к ApplicationServices, пока пользователь не введет настоящую команду, чтобы сделать что-то, что могло бы потенциально изменить ситуацию.
Что мешает вам иметь слой ViewServices, используемый контроллером в вашем шаблоне презентации MVC для выполнения задач, о которых вы просите?