Дочерние структуры являются допустимыми вариантами использования в моделировании домена, и часто встречаются в рекурсивных понятиях, таких как группы, теги и т. Д., Как в вашем примере папок. И мне нравится иметь дело с ними как с чистыми коллекционными объектами на уровне предметной области, без намека на постоянную логику. При написании такой доменной логики мне нравится представлять, что я имею дело с объектами, как если бы они постоянно сохранялись в оперативной памяти.
Я рассмотрю ваш рекурсивный пример для моего объяснения, но та же концепция применима к любой «коллекции» дочерних объектов, а не только к рекурсивным отношениям.
Ниже приведен пример реализации в псевдокоде с комментариями. Заранее извиняюсь, что код ближе к Python по структуре. Я хотел передать идею точно, и не беспокоиться о том, как представить ее в C #, в которой я не очень хорошо разбираюсь. Пожалуйста, задавайте вопросы о коде, если что-то не понятно.
Примечания к псевдокоду:
- На уровне домена вы имеете дело с коллекциями просто как с другим списком / коллекцией, не беспокоясь о сложностях, связанных с постоянством.
FolderService
- это ApplicationService
, который обычно вызывается API. Этот сервис отвечает за сборку инфраструктурных сервисов, взаимодействие с уровнем домена и возможное сохранение.
FolderTable
- это воображаемое представление базы данных объекта Folder
. FolderRepository
вместе знает об этом классе и подробностях его реализации
- Сложности сохранения и извлечения объекта Folder из БД будут присутствовать только в классе
FolderRepository
.
- Метод репозитория
load_by_name
загружает и заполняет все вложенные папки в родительскую папку. Мы можем преобразовать это в ленивую загрузку, только при доступе, и никогда не загружать ее, если мы не пересекаем ее (может даже быть разбит на страницы в зависимости от требований, особенно если нет конкретного ограничения на количество подпапок)
class Folder(AggregateRoot):
name: str
folders: List[Folder]
@classmethod
def construct_from_args(cls, params):
# This is a factory method
return Folder(**params)
def add_sub_folder(self, new_folder: Folder) -> None:
self.folders.append(new_folder)
def remove_sub_folder(self, existing_folder: Folder) -> None:
# Dummy implementation. Actual implementation will be more involved
for folder in self.folders:
if folder.name == existing_folder.name:
self.folders.remove(existing_folder)
class FolderService(ApplicationService):
def add_sub_folder(parent_folder_name: str, new_folder_params: dict) -> None:
folder_repo = _system.get_folder_repository()
parent_folder = folder_repo.load_by_name(parent_folder_name)
new_sub_folder = Folder.construct_from_args(new_folder_params)
parent_folder.add_sub_folder(new_sub_folder)
folder_repo.save(parent_folder)
class FolderTable(DBObject):
# A DB representation of the folder domain object
# `parent_id` will be empty for the root folder
name: str
parent_id: integer
class FolderRepository(Repository):
# Constructor for Repository
# that has `connection`s to the database, for later use
# FolderRepository works exclusively with `FolderTable` class
def load_by_name(self, folder_name: str) -> Folder:
# Load a folder, including its subfolders, from database
persisted_folder = self.connection.find(name=folder_name)
parent_identifier = persisted_folder.id
sub_folders = self.connection.find(parent_identifier)
for sub_folder in sub_folders:
persisted_folder.folders.append(sub_folder)
return persisted_folder
def save(self, folder: Folder) -> None:
persisted_folder = self.connection.find(name=folder.name)
parent_identifier = persisted_folder.id
# Gather the persisted list of folders from database
persisted_sub_folders = self.connection.find(parent_identifier)
for sub_folder in folder.folders:
# The heart of the persistence logic, with three conditions:
# If the subfolder is already persisted,
# Do Nothing
# If there is a persisted subfolder that is no longer a part of folders,
# Remove it
# If the subfolder is not among those already persisted,
# Add it
Если вы видите дыры в этой реализации или моем мыслительном процессе, пожалуйста, укажите на них.