【问题标题】:How do you model an operation saving an image using DDD?如何使用 DDD 对保存图像的操作进行建模?
【发布时间】:2018-06-05 14:29:12
【问题描述】:

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

图像不会本地存储在网络服务器中,它们需要上传到其他地方(即:云存储)。

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

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

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

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

【问题讨论】:

  • KISS:上传图片加载聚合并仅发送图片元数据和 storageId(没有实际图片数据)
  • 命令用于编排域中实现的逻辑。这看起来像是图像上传和聚合更新的编排。我同意上面的康斯坦丁,虽然我会先生成一个图像 storageID,启动异步上传,然后更新聚合。如果上传出现问题,聚合应该能够处理丢失的图像。

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


【解决方案1】:

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

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

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

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

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

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

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

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

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

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

  • 如果您采用悲观的方法,Success 将使 Profile 聚合脱离其挂起状态。
  • 如果您采用乐观的方法,则无需在 Success 中处理。
  • 在任何情况下,都应通过将 Profile 聚合回滚到图片的更新前状态来处理上传失败。

【讨论】:

  • @Pedro_goes,我得到了你的建议,但我的问题是我应该如何将文件(本身)传输到应用层。我应该将它作为“字节”或“FileStream”或“HttpPostedFile”或其他什么形式发送吗?
【解决方案2】:

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

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

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

【讨论】:

  • 如果保存到数据库失败会发生什么,图像会保持孤立状态吗?我应该如何触发将删除它的补偿动作?
  • @Vlad 是的,您需要一种检测孤立图像的方法,但这应该不是不可能的。您可以以某种方式记录数据库异常或查看最新的图像上传并将它们与配置文件相关联。
  • 在失败的情况下域触发一个参考失败图像的事件。应用程序处理此事件并删除已上传的文件或执行其他一些回滚操作
【解决方案3】:

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-04
    • 1970-01-01
    • 2020-11-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多