Доменные объекты и сервисы

На этот вопрос кто-то отвечает: "Вы никогда не позволяете реализациям доменного объекта сами вызывать службы!". Является ли это утверждение жестким правилом DDD или оно зависит от вашего собственного приложения и архитектуры?

Придуманный пример:

В качестве примера давайте предположим, что у нас есть UserImage объект в нашей модели, который заполняется из загруженного изображения пользователем. А затем давайте предположим, что мы можем отправить это изображение сторонней службе, которая может идентифицировать отпечатки большого пальца и вернуть Guid если совпадение найдено.

public IThumbPrintService {
    Guid FindMatch(Bitmap image);
}

public class UserImage {
    public Bitmap Image {get; set;} 
    public Guid ThumbPrintId {get; set;}
    public bool FindThumbPrintMatch() {
       // Would you call the service from here?
       ThumbPrintId = _thumbPrintService.FindMatch(this.Image);
       return ! ThumbPrintId.CompareTo(Guid.Empty);
    }
}

public class RoboCopUserImageService : IUserImageService {
     // Or move the call to a service method 
     // since it depends on calling a separate service interface
     public bool FindThumbPrintMatch(UserImage userImage) {
        userImage.ThumbPrintId = _thumbPrintService.FindMatch(userImage.Image);
        return !userImage.ThumbPrintId.CompareTo(Guid.Empty);            
     }
}

Чего можно избежать или получить, не позволяя объектам домена самим вызывать сервисы?

РЕДАКТИРОВАТЬ: Есть ли хорошие онлайн-статьи, которые обсуждают эту конкретную тему?

4 ответа

Решение

Это загадка электронной таблицы: набирает ли телефон номер телефона или сам номер телефона набирает номер на телефоне?

Вам может показаться, что Double Dispatch будет интересным чтением, хотя в вашей ситуации я считаю это излишним.

Принцип Единой Ответственности часто противоречит принципу ОО " Скажи, не спрашивай". Мои чувства по поводу предмета колебались, и я остановился на следующих условиях, когда логика должна входить в предметный объект:

В вашей ситуации я бы предпочел не помещать вызов в службу внутри объекта-сущности, главным образом потому, что эта служба не связана с вашим доменом, а скорее связана с постоянством. Доменные объекты должны быть связаны с концепциями домена, и я не думаю, что предоставленная вами услуга соответствует требованиям.

Например, я думаю, что вызов службы в объекте может быть приемлемым, если ваше приложение использует сторонний сервер рабочих процессов для управления частями его состояния. По сути, это шаблон состояний с состояниями, определенными во время выполнения.

Я думаю, что допустимо, чтобы domainObject.moveToNextState() (предполагая, что этот код "имеет смысл" на вашем вездесущем языке) вызывал службу, которая общается с вашим сервером, потому что сервер рабочих процессов управляет частью модели домена.

Добавлю, что DDD очень заинтересован в следовании языку домена. Слышите ли вы, что эксперты домена говорят: "Пользовательское изображение обнаруживает, соответствует ли его отпечаток большого пальца тем, которые есть в службе поставщиков XYZ"? Или они говорят: "Служба поставщика XYZ, учитывая отпечаток большого пальца, указывает, существует ли этот отпечаток большого пальца"? Выберите тот, который имеет больше смысла в вашей области.

Еще несколько мыслей (я много думал об этой проблеме, потому что она занимает центральное место в дизайне):

  • В книге DDD Evans сущность Account имеет такие методы, как credit(Amount), debit(Amount), TransferTo(Account, Amount) и accrue(), но у FundsTransferService есть метод Transfer(Account, Account, Amount). Метод TransferTo не вызывает никакой службы, а просто обрабатывает логику, которая включает в себя учетные записи, такие как зачисление и списание правильных сумм.

    FundsTransferService, в дополнение к координации, имеет свои собственные правила для проверки, правила, которые не вписываются в учетные записи. Точная сумма кредита или дебета может быть связана с внешними сторонами. Это делает неудобным для TransferTo вызывать услугу.

  • Для простых объектов, таких как UserImage, существенная доменная логика, которая может вписаться в сам объект, может быть недостаточной, потому что это, насколько я могу сказать, Агрегат. Я думаю, что агрегаты предоставляют больше возможностей для размещения логики предметной области. Пример учетной записи, скорее всего, является совокупным.

Я вижу один недостаток, заключающийся в том, что разрешение вашему объекту домена вызывать службы может усложнить сериализацию или, по крайней мере, вызвать некоторые проблемы после сериализации, когда кто-то на другой стороне вызывает его метод (ы) службы.

Я думаю, что лучше не вызывать репозитории или сервисы из сущностей или объектов-значений, но иногда это необходимо, например, если сущность должна вернуть другую сущность, которая должна быть загружена из базы данных, но она не может перейти к ней с помощью графа объектов. Тогда на помощь приходит принцип инверсии зависимостей, который означает, что сущности и объекты значений зависят от интерфейсов служб и репозиториев, а не от реализаций.

Если вы позволяете объекту сущности вызывать службу, он выполняет две роли: объект данных и объект службы. Как правило, каждый объект должен нести ответственность не только за реализацию, но и за использование.

В вашем случае непритязательный UserImage выглядит как Image и ThumbPrint Recognizer.

Другие вопросы по тегам