У вас есть два разных «действия», связанных с изображениями. У вас есть «физический» процесс и «логический» процесс. Логический процесс заключается в сохранении информации об изображении в хранилище домена, поскольку оно является частью домена. Физический процесс добавления (и удаления) является необходимым условием для логического процесса.
Делая шаг назад, физический процесс полностью независим от логического процесса, но обратное неверно. Вы, очевидно, не хотите сохранять мета-информацию об изображении (в домене), если изображение не было сохранено. Кроме того, вы не хотите удалять информацию из домена, если не можете удалить физический файл.
Домен должен содержать информацию, необходимую для удаления логического экземпляра изображения из источника данных. Думайте о домене как о физически отдельном приложении. В этом случае домен фактически не знает, что сохраняемые им данные имеют какое-либо отношение к физическому файлу. Обязательно сохраните это так.
Как правило, у меня есть сущности в сборке, затем мои репозитории и доменные службы в другой. Службы приложений живут вне доменной модели, но используют ее для выполнения своей работы. Таким образом, службы приложений используют одну или службы домена, либо другие службы приложений, а службы домена могут использовать одно или несколько хранилищ.
Имея это в виду, у вас есть два места для фактической логики удаления и третье место для их координации. Вот как это будет работать, если бы я делал это. Служба домена будет использовать хранилище для логического удаления из базового источника данных (а также для извлечения, которое вам также потребуется). Он не знает ничего другого, кроме работы с экземпляром объекта домена. У меня также была бы служба приложений (вне домена), которая специально занималась удалением физического экземпляра. В качестве аргумента, я предполагаю, что у вас есть класс ImageRepository и класс ImageServices, которые содержат ваш репозиторий домена и ваши доменные службы, соответственно. Вашим ImageServices нужен метод Delete (), а также любые методы Find (), которые вы используете. Я обычно явно вызываю методы find как FindBy ... () (т.е. FindByKey (), FindByName () и т. Д.).
Вы не хотите удалять логический экземпляр, если вам не удалось удалить физический экземпляр, поэтому убедитесь, что у вас есть средство измерения успешности операции удаления для физического образа. В этом случае я бы, вероятно, применил какое-то пользовательское исключение (поскольку я считаю, что удаление файла - это стандартная операция, которая обычно не должна завершаться сбоем). Обычно это относится к сфере «управления». Поэтому обычно у меня есть служба приложений, которая называется что-то вроде «ImageManagementService». Для простоты этот сервис (поскольку он является частью приложения, а не домена) может иметь частный метод для физического удаления. Давайте назовем это «DeleteImageFile ()».
Третье место - это координация этих двух операций, а также в качестве службы приложений. Я бы просто сделал это публичным методом в ImageManagementService. Мы можем назвать это «RemoveImage». Эта служба приложений будет выполнять следующие действия:
- Извлечение информации об экземпляре из доменных служб (сквозной вызов вашего хранилища).
- Используйте информацию об экземпляре, чтобы найти физический файл и удалить его (первая упомянутая служба приложений, снова).
- Если физическое удаление прошло успешно, удалите экземпляр (обратно в службу домена, снова обратившись к хранилищу).
Итак, происходит то, что само приложение вызывает метод «RemoveImage ()» из экземпляра «ImageManagementService». Внутри «RemoveImage ()» сначала вызывает «FindBy .. ()» из «ImageServices» домена, чтобы получить экземпляр из домена. Оттуда путь к файлу используется для вызова частного метода «DeleteImageFile ()» в экземпляре «ImageManagementService». В случае успеха он затем вызывает методы «Delete ()» в «ImageService» домена, который действует как фасад в ваш репозиторий.
Я думаю, что в этом случае очень важно сосредоточиться на разделении интересов, потому что, если у вас есть явное разделение (которое вы можете делать с различными сборками), вам будет комфортно знать, какая логика в какой место. Я очень рекомендую книгу Эвана. Кроме того, для быстрого ознакомления с концепцией SOC, связанной с DDD, я рекомендую взглянуть на серию из трех частей Джеффри Палермо, посвященную " Onion Architecture ".
Несколько замечаний о том, почему вы используете доменную службу вместо того, чтобы вызывать хранилище напрямую из службы приложений. Прежде всего, хранилище имеет более сложное создание экземпляров, чем служба домена. Помните, что это в основном фасад, но может иметь дополнительную логику, которая не вписывается нигде в области. Хорошим примером этого может быть, если вы хотите применить уникальное имя файла. Сам по себе объект домена не знает непосредственно о других объектах домена в других агрегатах, поэтому служба домена может проверить существующий экземпляр с тем же именем до операции сохранения. Действительно, очень удобно! Кроме того, служба домена не ограничивается одним хранилищем. У вас может быть доменная служба, координирующая усилия между несколькими репозиториями. Если у вас есть перекрывающиеся агрегаты, вам может потребоваться вызвать работу с двумя связанными корнями агрегатов одновременно. Вы можете сделать это в доменной службе, сохраняя подобную логику в домене и не впуская в приложение.
Надеюсь, это поможет. Я уверен, что есть и другие способы сделать это, но именно так я добился успеха в своих собственных приложениях с похожими сценариями.