Заводы, услуги, хранилище в DDD

У меня есть несколько вопросов относительно фабрик, репозиториев и сервисов в DDD. У меня есть следующие объекты: папка, файл, FileData.

По моему мнению, "Папка" является совокупным корнем и должна отвечать за создание объекта File и FileData.

Итак, мой первый вопрос: должен ли я использовать фабрику для создания этого агрегата или это зависит от хранилища? В настоящее время у меня есть 2 репозитория, один для папки и другой для файла, но мне кажется, что я должен объединить их вместе. В следующем фрагменте кода показан мой репозиторий папок, расположенный на уровне моей инфраструктуры:

public class FolderRepository : IFolderRepository
{
    #region Fields

    private readonly IFolderContext _context;
    private readonly IUnitOfWork _unitOfWork;

    #endregion

    #region Constructor

    public FolderRepository(IUnitOfWork unitOfWork)
    {
        _unitOfWork = unitOfWork;
        _context = _unitOfWork.Context as IFolderContext;
    }

    #endregion

    public IUnitOfWork UnitOfWork
    {
        get { return _unitOfWork; }
    }

    public IQueryable<Folder> All
    {
        get { return _context.Folders; }
    }

    public Folder Find(Guid id)
    {
        return _context.Folders.Find(id);
    }

    public void InsertGraph(Folder entity)
    {
        _context.Folders.Add(entity);
    }

    public void InsertOrUpdate(Folder entity)
    {
        if (entity.Id == Guid.Empty)
        {
            _context.SetAdd(entity);
        }
        else
        {
            _context.SetModified(entity);
        }
    }

    public bool Delete(Guid id)
    {
        var folder = this.Find(id) ?? _context.Folders.Find(id);
        _context.Folders.Remove(folder);

        return folder == null;
    }

    public int AmountOfFilesIncluded(Folder folder)
    {
        throw new NotImplementedException();
        //return folder.Files.Count();
    }

    public void Dispose()
    {
        _context.Dispose();
    }
}

Затем я создал сервис на своем уровне приложений, он называется "IoService". У меня есть сомнения по поводу местоположения сервиса. Должен ли он быть перемещен на доменный уровень?

public class IoService : IIoService
{
    #region Fields

    private readonly IFolderRepository _folderRepository;
    private readonly IFileRepository _fileRepository;
    private readonly IUserReferenceRepository _userReferenceRepository;

    #endregion

    #region Constructor

    public IoService(IFolderRepository folderRepository, IFileRepository fileRepository, IUserReferenceRepository userReferenceRepository)
    {
        if(folderRepository == null)
            throw new NullReferenceException("folderRepository");
        if(fileRepository == null)
            throw new NullReferenceException("fileRepository");
        if (userReferenceRepository == null)
            throw new NullReferenceException("userReferenceRepository");

        _folderRepository = folderRepository;
        _fileRepository = fileRepository;
        _userReferenceRepository = userReferenceRepository;
    }

    #endregion

    #region Folder Methods

    /// <summary>
    /// Create a new 'Folder'
    /// </summary>
    /// <param name="userReference"></param>
    /// <param name="name"></param>
    /// <param name="parentFolder"></param>
    /// <param name="userIds">The given users represent who have access to the folder</param>
    /// <param name="keywords"></param>
    /// <param name="share"></param>
    public void AddFolder(UserReference userReference, string name, Folder parentFolder = null, IList<Guid> userIds = null, IEnumerable<string> keywords = null, bool share = false)
    {
        var userReferenceList = new List<UserReference> { userReference };

        if (userIds != null && userIds.Any())
        {
            userReferenceList.AddRange(userIds.Select(id => _userReferenceRepository.Find(id)));
        }

        var folder = new Folder
        {
            Name = name,
            ParentFolder = parentFolder,
            Shared = share,
            Deleted = false,
            CreatedBy = userReference,
            UserReferences = userReferenceList
        };

        if (keywords != null)
        {
            folder.Keywords = keywords.Select(keyword =>
                new Keyword
                {
                    Folder = folder,
                    Type = "web",
                    Value = keyword,
                }).ToList();
        }

        //insert into repository
        _folderRepository.InsertOrUpdate(folder);

        //save
        _folderRepository.UnitOfWork.Save();
    }

    /// <summary>
    /// Get 'Folder' by it's id
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    public Folder GetFolder(Guid id)
    {
        return _folderRepository.Find(id);
    }

    #endregion

    #region File Methods

    /// <summary>
    /// Add a new 'File'
    /// </summary>
    /// <param name="userReference"></param>
    /// <param name="folder"></param>
    /// <param name="data"></param>
    /// <param name="name"></param>
    /// <param name="title"></param>
    /// <param name="keywords"></param>
    /// <param name="shared"></param>
    public void AddFile(UserReference userReference, Folder folder, FileData data, string name, string title = "", IEnumerable<string> keywords = null, bool shared = false)
    {
        var file = new File
        {
            Name = name,
            Folder = folder,
            FileData = data,
            CreatedBy = userReference,
            Type = data.Type
        };

        if (keywords != null)
        {
            file.Keywords = keywords.Select(keyword =>
                new Keyword
                {
                    File = file,
                    Type = "web",
                    Value = keyword,
                }).ToList();
        }

        folder.Files.Add(file);
        folder.Updated = DateTime.UtcNow;

        _folderRepository.InsertOrUpdate(folder);

        //save
        _folderRepository.UnitOfWork.Save();
    }

    /// <summary>
    /// Get 'File' by it's id
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    public File GetFile(Guid id)
    {
        return _fileRepository.Find(id);
    }

    #endregion
}

Подведем итог: стоит ли использовать сервис для создания объекта папки. Или сервис должен использовать фабрику, которая отвечает за создание объекта и отправку созданного объекта в хранилище? Как насчет внедрения зависимостей в сервис, следует ли мне внедрять свои сервисы из уровня пользовательского интерфейса с помощью контейнеров IOC, таких как Unity, или просто жестко кодировать зависимости в сервисе?

Спасибо

1 ответ

Решение

Итак, мой первый вопрос: должен ли я использовать фабрику для создания этого агрегата или это до хранилища?

Фабрика отвечает за создание, а хранилище отвечает за постоянство. После восстановления хранилище будет эффективно создавать экземпляры. Однако часто этот процесс создания выполняется с отражением и не проходит через фабрику, чтобы предотвратить инициализацию, которая должна происходить только во время создания.

В настоящее время у меня есть 2 репозитория, один для папки и другой для файла, но мне кажется, что я должен объединить их вместе.

В DDD у вас будет хранилище для каждого агрегата. Этот репозиторий будет отвечать за сохранение всех сущностей и объектов-значений, которые являются частью совокупности.

У меня есть сомнения по поводу местоположения сервиса. Должен ли он быть перемещен на доменный уровень?

ИМО, сервис приложений может быть помещен в доменный уровень, поскольку он уже служит фасадом, и их объединение принесло бы преимущества сплоченности. Одна мысль о IoService это такие методы, как AddFile будет обычно параметризован совокупной идентичностью, а не экземплярами. Поскольку служба приложений уже ссылается на репозиторий, она может при необходимости загружать соответствующие агрегаты. В противном случае вызывающий код будет отвечать за вызов хранилища.

Должен ли я использовать сервис для создания объекта папки. Или сервис должен использовать фабрику, которая отвечает за создание объекта и отправку созданного объекта в хранилище?

IoService выглядит хорошо, как есть, за исключением предыдущего комментария о параметризации идентичности, а не экземпляров.

Как насчет внедрения зависимостей в сервис, следует ли мне внедрять свои сервисы из уровня пользовательского интерфейса с помощью контейнеров IOC, таких как Unity, или просто жестко кодировать зависимости в сервисе?

Это вопрос предпочтений. Если вы можете воспользоваться контейнером IoC, используйте его. Однако, не используйте это только, чтобы использовать это. Вы уже делаете внедрение зависимостей, просто без причудливого контейнера IoC.

ОБРАЗЕЦ

class File
{
    public File(string name, Folder folder, FileData data,  UserReference createdBy, IEnumerable<string> keywords = null)
    {
        //...
    }
}

...

class Folder
{
    public File AddFile(string name, FileData data, UserReference createdBy, IEnumerable<string> keywords = null)
    {
        var file = new File(name, this, data, createdBy, keywords)
        this.Files.Add(file);
        this.Updated = DateTime.UtcNow;
        return file;
    }
}

...

public void AddFile(UserReference userReference, Guid folderId, FileData data, string name, string title = "", IEnumerable<string> keywords = null, bool shared = false)
{
    var folder = _folderRepository.Find(folderId);
    if (folder == null)
        throw new Exception();

    folder.AddFile(name, data, keywords);

    _folderRepository.InsertOrUpdate(folder);
    _folderRepository.UnitOfWork.Save();
}

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

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