如何使用DDD模拟保存图像的操作?

时间:2018-06-05 14:29:12

标签: design-patterns domain-driven-design aggregateroot

基本上,用户想要更改其个人资料图片。 Web服务器收到针对/user/35435/profile/picture的已发布图像,因此需要保存数据,并更新配置文件对象的LastModification属性。

图像不会本地存储在Web服务器中,需要将其上传到其他位置(即:云存储)。

目前,每个操作都由Command或Query表示。命令在环境事务中运行(如SQL事务)。图像上传操作不是事务性的,但可以在出现错误时进行补偿操作(即:如果DB操作失败,则删除图像)。

在一个简单的实现中,创建一个包含当前日期和图像数据的命令。执行命令处理程序,它加载ProfileAggregate聚合,并执行ProfileAggregate.updateProfilePicture(imageUploader, image, currentDate)作为参数传递imageUploader域服务。在方法内部,上载图像并更新配置文件。命令处理程序保存数据库中的更改并返回。

我不喜欢在图片上传期间保持交易的事实。我不喜欢在聚合中进行非域模型操作(如上传图像)(即使聚合在其他地方调用)。

这种交互是否应该像两个以串行方式执行的独立命令一样建模,或者只要在具体实现中没有依赖关系就可以将任何内容放在聚合中。

3 个答案:

答案 0 :(得分:1)

您的命令处理程序可能位于您的体系结构的“应用程序层”中。因此,考虑到这一点,您会收到一个命令,并协调您的域/其他服务以满足它。

那就是说,我真的不喜欢两个不同的命令实现,从客户端的角度来看,他们只做一个动作。您可以创建一个代理命令(单个命令),并在其处理程序上生成两个命令(一个用于处理聚合,另一个用于处理上载)但这种方法将使您的API /模型在未来不那么直观。

我也不喜欢在Profile聚合中捆绑操作的上传部分,因为它似乎不是它的职责之一。

我在这里建议的是以下方法:

  • 个人资料聚合应负责接受图片。因此,它的操作 updateProfilePicture 将简单地评估图片元数据(也许你有一个规则,图片必须具有一定的大小或针对某种算法进行验证,以试图找到裸露,这种东西)以及目标 Profile 的内部状态,以允许操作发生并使用 lastUpdated 属性或其他内容更新其内部状态。

  • ImageUploadService 将为您提供独立的功能,以接收图像并相应地存储它。

  • 这两项操作都将独立进行,并最终通过消息传递保持一致。

因此,您的命令处理程序将:

  • 加载个人资料聚合
  • 调用 updateProfilePicture (如果达到无效状态将中止)。在这里,您可能希望将配置文件聚合保留在PendingUpload状态(如果您希望采用悲观方法进行上传),或者只是假设上传通常会成功(采用乐观方法)并采取失败时的补偿措施。
  • 直接和异步调用 ImageUploadService (触发并忘记)
  • 上传完成后,将对个人资料的更改提交

当ImageUploadService完成接收图像后,它将向Profile域发送一条消息,报告上传成功/失败。然后:

  • 如果您采取了悲观的方法,则成功会将个人资料聚合从其待处理状态中删除。
  • 如果你采取乐观的方法,成功就不需要处理。
  • 在任何情况下,都应通过将配置文件聚合回滚到图片的更新前状态来处理上传失败。

答案 1 :(得分:1)

对您有帮助的主要问题是,当图像无法上传时,系统应该怎么做。我认为答案是您希望用户收到错误,系统(配置文件)应保持不变。

从Domain的角度来看,实际的图像上传/存储过程是无关紧要的。此操作实际上是基础结构的一部分,应该在命令发送到域层(到配置文件聚合)之前由应用层编排。这样,如果上载因任何原因失败,则不会发送该命令。

图像上传可以同步或异步完成;这取决于你的平台/编程语言/等,它是无关紧要的。关键是只有在图像上传成功后才将命令发送到Aggregate。该命令应包括ImageStorageId和可能的一些元数据(如文件名,文件大小,图像大小等)。

答案 2 :(得分:0)

我目前坚持在运行域命令之前处理文件上传。

除非域模型需要直接处理图像(或其他一些)文件,否则让所有基于文件的操作(包括上传和裁剪)远离它似乎是合理的。因此,如果您不分析模型中图像中的数据(例如,我不知道,人脸检测?),而只需要一些抽象的个人资料图像,则将元数据对象传递给模型。

如果域模型出现故障,它会调度一个事件,由应用程序处理并执行一些回滚逻辑。