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

How do you model an operation saving an image using DDD?

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

图片没有本地存储在网络服务器中,需要上传到其他地方(即:云存储)。

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

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

我不喜欢在图片上传过程中进行交易。我不喜欢从聚合中执行非域模型操作(如上传图像)(即使聚合正在调用其他地方)。

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

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

也就是说,我不太喜欢两个不同的命令实现,因为从客户的角度来看,它们只执行一个操作。您可以创建一个代理命令(单个命令)并在其处理程序上生成两个命令(一个用于处理聚合,另一个用于处理上传),但这种方法将来会使您的 API/Model 不那么直观。

我也不喜欢将操作的上传部分捆绑在配置文件聚合中,因为它看起来根本不是它的职责之一。

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

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

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

  • 这两个操作将独立发生,并最终通过消息传递保持一致。

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

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

当 ImageUploadService 完成接收图像时,它会向配置文件域发送一条消息,报告上传 success/failure。那么:

  • 如果您采取悲观的方法,成功将使 Profile 聚合脱离其待定状态。
  • 如果您采用乐观的方法,则无需处理即可获得成功。
  • 在任何情况下,都应通过将 个人资料 聚合回滚到图片更新前的状态来处理上传失败。

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

从域的角度来看,实际图像 uploading/storing 过程是无关紧要的。此操作实际上是基础架构的一部分,应在命令发送到域层(到配置文件聚合)之前由应用层编排。这样无论什么原因上传失败,命令都不会发送。

图片上传可以同步也可以异步;这取决于您的 platform/programming language/etc 并且无关紧要。关键是你只有在图片上传成功后才向聚合发送命令。该命令应包括 ImageStorageId 和可能的一些元数据(如文件名、文件大小、图像大小等)。

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

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

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