DDD и управление файлами - PullRequest
       27

DDD и управление файлами

0 голосов
/ 19 января 2019

У меня есть простая система управления документами, которую я собираю, я пытаюсь следовать твердым принципам DDD, и все складывается вместе. Одна из областей, в которых я сомневаюсь, - это то, что будет самым чистым решением для управления файлами

Для справки, большая часть этого управления документами будет вращаться вокруг загрузки документов и назначения их для определенных «рабочих заданий». Это в обрабатывающей промышленности, нам нужно отслеживать определенные документы и отправлять их клиенту, когда мы все закончим делать свои вещи.

Таким образом, мой ограниченный контекст в основном состоит из пары основных сущностей, скажем, DocumentPackage, Document, Requirements и т. Д. DocumentPackage - это группа документов для одного «рабочего задания». Один и тот же документ может использоваться в нескольких пакетах документов. Каждый DocumentPackage имеет определенное количество Требований, которые представляют собой отдельный тип документа, который необходим как часть пакета.

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

Вот пример команды UploadDocumentCommand и обработчика, которые у меня есть. Обратите внимание, что я решил сохранить загруженный файл в локальной файловой системе на контроллере API и передать его в моей команде как FileInfo.

public class UploadDocumentCommand : IRequest<AppResponse>
{
    public UploadDocumentCommand(Guid documentId, string workOrderNumber, FileInfo file, Guid? requirementId = null)
    {
        DocumentId = documentId;
        WorkOrderNumber = new WorkOrderNumber(workOrderNumber);
        FileInfo = file;
        RequirementId = requirementId;
    }

    public Guid DocumentId { get; }
    public WorkOrderNumber WorkOrderNumber { get; }
    public Guid? RequirementId { get; }
    public FileInfo FileInfo { get; }
}

public class UploadDocumentCommandHandler : IRequestHandler<UploadDocumentCommand, AppResponse>
{
    private readonly IDocumentRepository documentRepository;
    private readonly IDocumentPackageRepository packageRepo;

    public UploadDocumentCommandHandler(IDocumentRepository documentRepository, IDocumentPackageRepository packageRepo)
    {
        this.documentRepository = documentRepository;
        this.packageRepo = packageRepo;
    }
    public async Task<AppResponse> Handle(UploadDocumentCommand request, CancellationToken cancellationToken)
    {
        try
        {
            // get the package, throws not found exception if does not exist
            var package = await packageRepo.Get(request.WorkOrderNumber);

            var document = DocumentFactory.CreateFromFile(request.DocumentId, request.FileInfo);

            if (request.RequirementId != null)
            {
                package.AssignDocumentToRequirement(document, request.RequirementId.GetValueOrDefault());
            } 
            else
            {
                package.AddUnassignedDocument(document);
            }

            await documentRepository.Add(document, request.FileInfo);
            await packageRepo.Save(package);

            return AppResponse.Ok();
        }
        catch (AppException ex)
        {
            // the file may have been uploaded to docuware but failed afterwards, this should be addressed
            // this can be better handled by using an event to add the document to the package only after successful upload
            return AppResponse.Exception(ex); 
        }

    }
}

public class Document : Entity<Guid>
{
    private Document() { }
    public Document(Guid id, string fileName, DateTime addedOn)
    {
        Id = id;
        FileName = new FileName(fileName);
        AddedOn = addedOn;
    }
    public FileName FileName { get; private set; }
    public DateTime AddedOn { get; private set; }

    public override string ToString() => $"Document {Id} {FileName}";
}

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

public class DocumentRepository : IDocumentRepository
{
    private readonly DbContext context;
    private readonly IDocuwareRepository docuwareRepository;

    public DocumentRepository(DbContext context, IDocuwareRepository docuwareRepository)
    {
        this.context = context;
        this.docuwareRepository = docuwareRepository;
    }
    public async Task<Document> Get(Guid id)
    {
        return await 
            context
            .Document
            .FirstOrDefaultAsync(x => x.Id.Equals(id));
    }

    public async Task Add(Document document, FileInfo fileInfo)
    {
        var results = await docuwareRepository.UploadToDocuware(document.Id, fileInfo);


        var storageInfo = new DocumentStorageInfo
        {
            DocuwareId = results.DocuwareDocumentId,
            DocuwareFileCabinetId = results.CabinetId,
            Document = document

        };

        context.DocumentStorageInfo.Add(storageInfo);
        context.Document.Add(document);

        await context.SaveChangesAsync();
    }

    public Task<FileStream> Download(Guid id)
    {
        throw new NotImplementedException();
    }

    public void Dispose()
    {
        context?.Dispose();
    }
}

У меня есть другой вариант использования, в котором я работаю над тем, где должен быть загружен DocumentPackage. Я хочу, чтобы мое приложение взяло все действительные документы из пакета, скомпилировало их в zip-файл, где документы структурированы в иерархии папок в соответствии с требованиями, и этот архивный zip-файл будет сохранен в течение длительного времени по причинам прослеживаемости, и клиент может загрузить этот zip-файл. Итак, у меня есть другая сущность, которую я называю DocumentPackageArchive, у нее есть репозиторий, и я думаю, что zip-файл компилируется там. Это потребовало бы, чтобы хранилище загрузило все файлы из соответствующих хранилищ, сжало его в виде zip-файла, сохранило zip-файл локально на веб-сервере, отправило zip-файл обратно для сохранения только для чтения и обновило базу данных некоторыми данными. данные об архиве. Да, я намеренно создаю копию пакета документов в виде снимка.

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

Как бы вы посоветовали мне подойти к этому? Я чувствую, что эти два хранилища, имеющие дело с документами, должны быть переработаны.

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

...