Интерфейс с сервисным уровнем или самими объектами домена? (DDD)
Я все еще изучаю DDD, и у меня есть два (возможно, простых) вопроса:
Если Фабрика создает новые экземпляры объектов / графиков / агрегатов, но также "восстанавливает" объекты / графы из репозитория, то:
(1) Вызывает ли функция / задания / задачи / единицу работы вашего уровня обслуживания на Фабрику или поведенческий метод в экземпляре Entity или функцию DomainService? Я заблудился относительно стека вызовов, основанного на ответственности этих компонентов.
(2) Есть ли у экземпляров сущностей даже "поведенческие методы", как описано выше? Например, имеет ли пост p.UpdatePost(string bodyText)
или это не касается модели предметной области и поэтому то же самое должно быть достигнуто с помощью репозитория? Или функция сервисного уровня, должна ли она вызывать репозиторий в этом случае, а у экземпляра сущности просто есть поведенческие методы, специфичные для домена, а не постоянства? Но тогда почему звучит так, будто "обновление сообщения" является функцией домена, если это цель пользователя?
Вы можете видеть, что я повсюду. Пожалуйста помоги.
1 ответ
(1) Вызывает ли функция / задания / задачи / единицу работы вашего уровня обслуживания на Фабрику или поведенческий метод в экземпляре Entity или функцию DomainService? Я заблудился относительно стека вызовов, основанного на ответственности этих компонентов.
Обычно - верхний уровень извлекает необходимый агрегатный корень и вызывает на нем функцию. Иногда верхний уровень извлекает несколько агрегатных корней и передает их службе домена, но не часто, потому что служба домена является довольно убедительным признаком того, что существует нераспознанный агрегатный корень. В конце - верхний уровень обеспечивает сохранение совокупного корня.
(2) Есть ли у экземпляров сущностей даже "поведенческие методы", как описано выше? Например, имеет ли Post p.UpdatePost(string bodyText) или это не касается модели предметной области, и поэтому то же самое должно быть достигнуто с помощью репозитория? Или функция сервисного уровня, должна ли она вызывать репозиторий в этом случае, а у экземпляра сущности просто есть поведенческие методы, специфичные для домена, а не постоянства? Но тогда почему звучит так, будто "обновление сообщения" является функцией домена, если это цель пользователя?
Да, они делают. Доменная модель должна знать об изменениях своего состояния. И это гораздо выгоднее, как кажется на первый взгляд. Самое замечательное в этом то, что вы получаете балл расширяемости. Если клиент через неделю перейдет к Вам и скажет, что он хочет, чтобы система проверяла дополнительные вещи, когда пользователь обновляет сообщение, - вместо поиска каждой строки post.bodyText="new value"
, Вы сможете идти прямо к post.UpdatePost
метод и прикрепить необходимые вещи там.
С другой стороны - CRUD не является взаимоисключающим с доменным дизайном. Например, в моем приложении управление пользователями и их ролями неинтересно, и я даже не пытаюсь их детально моделировать. Вы должны распознать части, которые важны для бизнеса. Ваше приложение описывает и работает с ним.
Имейте в виду, что управляемый доменом дизайн имеет смысл только для сложных приложений. Простое приложение блога не нуждается в этом.
(3) Я ошибаюсь, полагая, что сервисный уровень (не доменные сервисы) должен инкапсулировать, как интерфейс взаимодействует с доменным уровнем?
На мой взгляд, сервисы приложений больше предназначены для организации инфраструктуры. Если инфраструктура не задействована - служба приложений теряет ценность:
Прикладные услуги в основном это просто фасады. И каждый фасад плох, если сложность добавляет сложностей, которые он решает.
Внутри домена:
//aggregate root is persistence ignorant.
//it shouldn't reference repository directly
public class Customer{
public string Name {get; private set;}
public static Customer Register(string name){
return new Customer(name);
}
protected Customer(string name){
//here it's aware of state changes.
//aggregate root changes it's own state
//instead of having state changed from outside
//through public properties
this.Name=name;
}
}
//domain model contains abstraction of persistence
public interface ICustomerRepository{
void Save(Customer customer);
}
За пределами домена:
public class CustomerRepository:ICustomerRepository{
//here we actually save state of customer into database/cloud/xml/whatever
public void Save(Customer customer){
//note that we do not change state of customer, we just persist it here
_voodoo.StoreItSomehow(customer);
}
}
//asp.net mvc controller
public class CustomerController{
public CustomerController(ICustomerRepository repository){
if (repository==null)throw new ArgumentNullException();
_repository=repository;
}
public ActionResult Register(string name){
var customer=Customer.Register(name);
_repository.Save(customer);
}
}