DDD和文件管理

时间:2019-01-19 00:58:16

标签: c# events asp.net-core domain-driven-design cqrs

我有一个简单的文档管理系统,正在组装中,我正在尝试遵循扎实的DDD原则,并且一切都变得很顺利。我一直在质疑的一个领域是,用于管理文件的最干净的解决方案是什么。

对于背景,很多此类文档管理将围绕上载文档并将它们分配给特定的“工作单”。这是在制造业中,我们需要跟踪某些文档,并在完成制作工作后将其发送给客户。

因此,我的有限上下文主要由几个主要实体组成,比如DocumentPackage,Document,Requirements等。DocumentPackage是针对单个“工作订单”的文档分组。同一文档可以在多个DocumentPackages中使用。每个DocumentPackage都有一定数量的Requirements,这是作为包的一部分需要的不同类型的文档。

因此,当涉及上传和下载文件,处理文件以及更新数据库以反映这些更改的操作时,我最想在哪里处理大多数操作?

这是我有一个UploadDocumentCommand和Handler的示例。请注意,我决定将上传的文件保存到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保存在Web服务器上本地,将zip文件发送回以进行只读保存,并使用一些更新数据库。有关档案的数据。是的,我有意将文档包的副本创建为快照。

因此,这让我感到困惑,我感觉到文件管理无处不在。我将某些文件保存在Web API中,因为当我从IFormFile获取文件时,我需要将它们保存到临时存储中。我正在处理应用程序层中的文件信息,作为命令的一部分。而且大多数繁重的工作都发生在基础架构层。

您如何建议我采取这种方法?我觉得这两个处理文档的存储库需要重新设计。

作为补充说明,我也正在考虑通过领域事件来协调某些工作。我认为我还没有为此做好准备,这似乎比现在需要添加的要复杂得多。不过,在该领域的建议也将受到赞赏。

0 个答案:

没有答案