Fah*_*ahd 2 design-patterns domain-driven-design
假设您有一个像这样的简单聚合根:
Playlist {
String name
List<Song> songs
add(Song song) {
// Some business rules
songs.add(song)
}
}
Run Code Online (Sandbox Code Playgroud)
现在假设您想要在add(Song)依赖于其他聚合根的方法中引入业务规则。例如:一首歌曲不能出现在超过 3 个播放列表中。实现此目的的一种方法是在应用程序层获取此信息(包含歌曲的播放列表的数量)并将其传递给该add(Song)方法。
但现在进一步假设该业务规则仅适用于某些条件。例如,想象一下名称以“M”开头的播放列表没有这样的限制(完全任意)。现在,在应用程序层获取信息意味着要么在错误的级别实现域逻辑,要么获取您不会使用的数据。随着业务规则变得更加复杂,成本也会变得更高。
现在显而易见的解决方案是:使用有权访问播放列表存储库的域服务并在那里执行逻辑。虽然这有效,但我想知道是否有任何模式/架构技巧/重组可以在不使用服务封装逻辑的情况下解决这个问题?
谢谢
我知道你说“没有服务”,但我决定列出我能想到的解决这个问题的所有各种方法(并非全部等效):
\nAR(聚合根)方法接收纯外部数据。正如您所说,这是最简单的策略,但对于更复杂的场景来说有点不足。它还将 AR 与特定类型的歌曲播放列表添加规则结合起来。
\nAR 方法接收策略/规则。AR 方法提供了一个策略(可能是不纯粹的)来执行规则。例如,您可以有一个SongPlaylistAdditionRule#check(Song, Playlist)界面。ARPlaylist只会呼吁rule.check(song, this)强制执行。应用程序层将利用某种规则提供程序来获取正确类型的规则,或者通过 IoC 容器注入正确的规则。
域服务。这个很明显了,就不解释了。
\n同步策略(事件处理程序)适用于同一事务。您将有一个SongPlaylistAdditionPolicy监听事件,例如SongAddedToPlaylist通过内存中的事件DomainEventPublisher,允许策略参与/执行同一事务。将SongPlaylistAssociationPolicy居住在域中。它与#2 和#3 类似,但是间接调用的。
最终一致的政策。与上面的概念相同,但会在另一个事务中异步执行。您很可能会允许违规行为,但随后通知用户该歌曲因违反政策而从播放列表中删除,或者采取任何其他类型的补偿操作。
\n传奇/流程管理器。您可以将此方法视为保留模式,在其中引入小的强一致过渡状态,以实现最终目标。每个状态转换通常在其自己的事务中进行处理,并在发生故障时通过补偿操作进行回滚。例如
\na) playlist.add(song)\xe2\x9f\xb6 触发SongAddedToPlaylist/抛出:检查播放列表特定的不变量(例如播放列表中歌曲的最大数量)
b) song.apply(songAddedToPlaylist)\xe2\x9f\xb6 触发SongAdditionToPlaylistConfirmed/ SongAdditionToPlaylistFailed:检查特定于歌曲的不变量(例如它属于多少个播放列表)
值得注意的是,在上述所有检查中,#1、#2、#3 和可能的#4 都可能是过时的检查,这意味着它们不一定能通过并发防止规则违规,除非您以某种方式锁定所有已被检查的数据。读入给定的事务。
\n#5 可以与 #1-#4 结合使用,以减少意外违规的次数,但也涵盖了通过并发违反规则的情况。
\n#6 可能是最自然的一种,因为从领域的角度来看,它几乎都是 AR。如果您不想处理最终一致性,您始终可以以类似于 #4 的方式进行集成,并在单个 TX 中修改所有 AR。在这种情况下,您也不必对过渡状态进行建模。然后,当您需要扩展时,您就会转向最终一致性。
\n希望这会给您一些关于如何有效解决您的具体问题的想法!没有一种解决方案适合所有情况!
\n| 归档时间: |
|
| 查看次数: |
722 次 |
| 最近记录: |