design-patterns - DDD - 没有域服务的条件业务规则
问题描述
假设您有一个像这样的简单聚合根:
Playlist {
String name
List<Song> songs
add(Song song) {
// Some business rules
songs.add(song)
}
}
现在假设您要在add(Song)
依赖于其他聚合根的方法中引入业务规则。例如:一首歌不能出现在超过 3 个播放列表中。一种方法是在应用程序层中获取此信息(包含歌曲的播放列表的数量)并将其传递给add(Song)
方法。
但是现在进一步假设该业务规则仅适用于某些条件。例如,想象一下名称以“M”开头的播放列表没有这样的限制(完全任意)。现在在应用层获取信息意味着要么在错误的级别实现域逻辑,要么获取你不会使用的数据。随着业务规则变得更加复杂,这将变得更加昂贵。
现在显而易见的解决方案是:使用可以访问播放列表存储库的域服务并在那里执行您的逻辑。虽然这可行,但我想知道是否有任何模式/架构提示/重组可以在不使用服务封装逻辑的情况下解决这个问题?
谢谢
解决方案
我知道您说“没有服务”,但我决定仅列出我能想到的解决此问题的所有各种方法(并非全部等效):
AR 方法接收纯外部数据。就像你说的,这是最简单的策略,但对于更复杂的场景来说有点不足。它还将 AR 与特定类型的歌曲播放列表添加规则相结合。
AR 方法接收一个策略/规则。AR 方法提供了一个策略(可能是不纯的)来执行规则。例如,您可以有一个
SongPlaylistAdditionRule#check(Song, Playlist)
接口。Playlist
AR 只会要求执行rule.check(song, this)
它。应用层将利用某种规则提供程序来获取正确类型的规则,或者将通过 IoC 容器注入正确的规则。域服务。这个很明显,不解释了。
同步策略(事件处理程序)适用于同一事务。您将拥有一个
SongPlaylistAdditionPolicy
监听事件,例如SongAddedToPlaylist
通过内存中DomainEventPublisher
允许策略参与/执行同一事务。将SongPlaylistAssociationPolicy
住在域中。它类似于#2 & #3,但间接调用。最终一致的政策。与上面相同的概念,但将在另一个事务中异步执行。您很可能会允许违规,但随后会通知用户该歌曲已从播放列表中删除,因为违反了政策或任何其他类型的补偿措施。
传奇/流程管理器。您可以将这种方法视为预留模式,在其中引入小的强一致过渡状态,朝着最终目标前进。每个状态转换通常在它自己的事务中处理,并在发生故障时通过补偿动作回滚。例如
a)
playlist.add(song)
⟶ 触发SongAddedToPlaylist
/抛出:检查播放列表特定的不变量(例如播放列表中的最大歌曲数)b)
song.apply(songAddedToPlaylist)
⟶ 触发SongAdditionToPlaylistConfirmed
/SongAdditionToPlaylistFailed
:检查歌曲特定的不变量(例如它属于多少个播放列表)
需要注意的是,在上述所有内容中,#1、#2、#3 和可能的 #4 都可能是过时的检查,这意味着它们不一定会通过并发来防止违反规则,除非您以某种方式锁定了所有已保存的数据读入给定的事务。
#5 可以与 #1-#4 结合使用,以减少意外违规的数量,但也涵盖了通过并发违反规则的场景。
#6 可能是最自然的一个,因为从领域的角度来看,它几乎都是 AR。如果您不想处理最终的一致性,您可以始终以类似于 #4 的方式进行集成,并将所有 AR 修改在单个 TX 中。在这种情况下,您也不必对过渡状态进行建模。然后,当您需要扩展时,您将转向最终的一致性。
希望这会给您一些有关如何有效解决特定问题的想法!没有一个适合所有的解决方案!
推荐阅读
- android - 有没有办法仅在使用 Android API 级别 28 上的 KeyStore 的软件中存储加密密钥?
- javascript - JavaScript 中的面向对象编程:如何从其他文件调用函数?
- python - 理解下面这段代码
- angular - 为什么我无法使用具有管理员访问权限和正确策略集的 AWS Amplify storage.get 访问我的 s3 私有对象。收到 403 错误
- python - Python 十六进制字符串到带反斜杠的十六进制字符串
- reactjs - 无法让 date-fns 在 material-ui/react 中工作。不断出现找不到错误
- arrays - Cloned arrays affecting react state
- php - 无法确定属性的访问类型...新值必须是数组或 \Traversable 的实例,给定“App\Entity\Role”
- c# - 如何将文件流变量传递给另一个方法?
- botframework - 未使用 REST API 调用生成知识库 ID