Позвольте мне начать с того, что сначала извиняюсь за длину поста, но я действительно хотел передать как можно больше подробностей, чтобы не тратить время на комментарии в комментариях.
Я разрабатываю приложение, следуя подходу DDD, и мне интересно, каким руководством я могу следовать, чтобы определить, должен ли Совокупный корень содержать другой AR или они должны быть оставлены в качестве отдельных "автономных" AR.
Возьмите случай с простым приложением Time Clock, которое позволяет Сотрудникам самим входить или выходить в течение дня. Пользовательский интерфейс позволяет им вводить идентификатор сотрудника и PIN-код, который затем проверяется и восстанавливается текущее состояние сотрудника. Если сотрудник в данный момент работает, пользовательский интерфейс отображает кнопку «Clock Out»; и, наоборот, если они не синхронизированы, кнопка показывает «Clock In». Действие, выполняемое кнопкой, также соответствует состоянию работника.
Приложение представляет собой веб-клиент, который вызывает внутренний сервер, предоставляемый через интерфейс службы RESTful. Мой первый опыт создания интуитивно понятных и удобных для чтения URL привел к следующим двум конечным точкам:
http://myhost/employees/{id}/clockin
http://myhost/employees/{id}/clockout
ПРИМЕЧАНИЕ. Они используются после проверки идентификатора и PIN-кода сотрудника, а в заголовке передается «токен», представляющий «пользователя». Это связано с тем, что существует «режим менеджера», который позволяет менеджеру или руководителю включать или выключать другого сотрудника. Но ради этой дискуссии я стараюсь сделать это простым.
На сервере у меня есть ApplicationService, который предоставляет API. Моя первоначальная идея для метода ClockIn что-то вроде:
public void ClockIn(String id)
{
var employee = EmployeeRepository.FindById(id);
if (employee == null) throw SomeException();
employee.ClockIn();
EmployeeRepository.Save();
}
Это выглядит довольно просто, пока мы не поймем, что информация о тайм-карте сотрудника фактически поддерживается в виде списка транзакций. Это означает, что каждый раз, когда я вызываю ClockIn или ClockOut, я не изменяю напрямую состояние Сотрудника, а вместо этого добавляю новую запись в Табель рабочего времени Сотрудника. Текущее состояние Сотрудника (синхронизировано или нет) основано на самой последней записи в Табеле рабочего времени.
Итак, если я воспользуюсь кодом, показанным выше, мой репозиторий должен признать, что постоянные свойства Сотрудника не изменились, но что новая запись была добавлена в Табель рабочего времени Сотрудника и выполнить вставку в хранилище данных.
С другой стороны (и вот главный вопрос поста), TimeSheet выглядит так, как будто это Aggregate Root, так как у него есть идентичность (идентификатор сотрудника и период), и я мог бы так же легко реализовать ту же логику, что и TimeSheet.ClockIn. (EmployeeID).
Я обсуждаю достоинства двух подходов и, как указано во вступительном абзаце, задаюсь вопросом, какие критерии мне следует оценить, чтобы определить, какой подход больше подходит для этой проблемы.