В настоящее время я изучаю DDD, и у меня возникли некоторые вопросы относительно того, как управлять репозиториями с DDD.
На самом деле, я встретил две возможности:
Первый
Первый способ управления службами, которые я прочитал, состоит в том, чтобы внедрить репозиторий и модель домена в службу приложений.
Таким образом, в одном из методов службы приложения мы вызываем метод службы домена (проверка бизнес-правил), и, если условие выполняется, вызывается специальный репозиторий для сохранения / извлечения объекта из базы данных.
Простой способ сделать это может быть:
class ApplicationService{
constructor(domainService, repository){
this.domainService = domainService
this.repository = repository
}
postAction(data){
if(this.domainService.validateRules(data)){
this.repository.persist(new Entity(data.name, data.surname))
}
// ...
}
}
Второй
Вторая возможность состоит в том, чтобы вместо этого внедрить репозиторий внутри domainService и использовать репозиторий только через службу домена:
class ApplicationService{
constructor(domainService){
this.domainService = domainService
}
postAction(data){
if(this.domainService.persist(data)){
console.log('all is good')
}
// ...
}
}
class DomainService{
constructor(repository){
this.repository = repository
}
persist(data){
if(this.validateRules(data)){
this.repository.save(new Entity(data.name))
}
}
validateRules(data){
// returns a rule matching
}
}
Отныне я не могу отличить, какой из них лучший (если есть один лучший) или что они подразумевают в своем контексте.
Можете ли вы привести пример, где один может быть лучше другого и почему?
источник
Ответы:
Краткий ответ - вы можете использовать репозитории из службы приложений или службы домена, но важно учитывать, почему и как вы это делаете.
Назначение доменной службы
Доменные службы должны инкапсулировать понятия / логику домена - как таковой, метод доменной службы:
не принадлежит доменной службе, поскольку
persist
не является частью вездесущего языка, а операция постоянства не является частью бизнес-логики домена.Обычно доменные службы полезны, когда у вас есть бизнес-правила / логика, которые требуют координации или работы с несколькими агрегатами. Если логика включает только один агрегат, он должен быть в методе на объектах этого агрегата.
Репозитории в Службах Приложений
Таким образом, в этом смысле, в вашем примере, я предпочитаю ваш первый вариант - но даже там есть возможности для улучшения, так как ваша доменная служба принимает необработанные данные из API - почему доменная служба должна знать о структуре
data
? Кроме того, данные, по-видимому, связаны только с одним агрегатом, поэтому использование службы домена для этого имеет ограниченную ценность - обычно я бы помещал проверку в конструктор сущностей. напримери выбросить исключение, если оно недействительно. В зависимости от вашей прикладной среды может быть просто иметь согласованный механизм для перехвата исключения и сопоставления его с соответствующим ответом для типа API - например, для API REST, вернуть код состояния 400.
Репозитории в доменных службах
Несмотря на вышесказанное, иногда полезно внедрить и использовать репозиторий в доменной службе, но только если ваши репозитории реализованы таким образом, что они принимают и возвращают только корни агрегатов, а также в тех случаях, когда вы абстрагируете логику, которая включает в себя несколько агрегатов. например
реализация службы домена будет выглядеть так:
Вывод
Ключевым моментом здесь является то, что доменная служба инкапсулирует процесс, который является частью вездесущего языка. Чтобы выполнять свою роль, ему нужно использовать репозитории - и это прекрасно.
Но добавление доменной службы, которая оборачивает хранилище с помощью метода с именем,
persist
добавляет мало пользы.Исходя из этого, если ваша прикладная служба выражает вариант использования, который требует работы только с одним агрегатом, нет проблем с использованием хранилища непосредственно из службы приложений.
источник
Есть проблема с принятым ответом:
Доменная модель не может зависеть от хранилища, и доменная служба является частью доменной модели -> доменная служба не должна зависеть от хранилища.
Вместо этого вам следует собрать все ваши сущности, необходимые для выполнения бизнес-логики, уже в службе приложений, а затем просто предоставить вашим моделям созданные объекты.
Исходя из вашего примера это может выглядеть так:
Итак, практическое правило: модель предметной области не зависит от внешних слоев
Приложение против доменного сервиса Из этой статьи :
Доменные сервисы очень детализированы, тогда как сервисы приложений являются фасадом, предназначенным для предоставления API.
Доменные сервисы содержат доменную логику, которая не может быть естественным образом помещена в сущность или объект-значение, тогда как сервисы приложений управляют выполнением доменной логики и сами не реализуют никакой доменной логики.
Методы службы домена могут иметь другие элементы домена в качестве операндов и возвращаемых значений, тогда как службы приложений работают с тривиальными операндами, такими как значения идентичности и примитивные структуры данных.
Сервисы приложений объявляют зависимости от инфраструктурных сервисов, необходимых для выполнения логики домена.
источник
Ни один из ваших шаблонов не годится, если ваши сервисы и объекты не заключают в себе какой-то согласованный набор ответственности.
Прежде всего, скажите, что представляет собой ваш доменный объект, и расскажите, что он может делать на языке домена. Если он может быть действительным или недействительным, почему бы не использовать его как свойство самого объекта домена?
Если, например, валидность объектов имеет смысл только с точки зрения другого объекта, то, возможно, у вас есть ответственность «правило проверки X для доменных объектов», которое может быть инкапсулировано в набор сервисов.
Требуется ли проверка объекта в соответствии с вашими бизнес-правилами? Возможно нет. Ответственность за «хранение объектов» обычно лежит в отдельном объекте хранилища.
Теперь у вас есть операция, которую вы хотите выполнить, которая охватывает диапазон обязанностей, создать объект, проверить его и, если он действителен, сохранить.
Эта операция присуща доменному объекту? Затем сделайте его частью этого объекта, т.е.
ExamQuestion.Answer(string answer)
Это соответствует какой-то другой части вашего домена? положи это там
Basket.Purchase(Order order)
Вы бы предпочли услуги ADM REST? Хорошо, тогда.
источник