我有一个简单的文档管理系统,正在组装中,我正在尝试遵循扎实的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获取文件时,我需要将它们保存到临时存储中。我正在处理应用程序层中的文件信息,作为命令的一部分。而且大多数繁重的工作都发生在基础架构层。
您如何建议我采取这种方法?我觉得这两个处理文档的存储库需要重新设计。
作为补充说明,我也正在考虑通过领域事件来协调某些工作。我认为我还没有为此做好准备,这似乎比现在需要添加的要复杂得多。不过,在该领域的建议也将受到赞赏。