我来自 OO 背景(C#、javascript),Scala 是我第一次涉足 FP。
由于我的背景,我无法实现一个非常适合我的域问题并且也符合 FP 的良好实践的域模型,例如代码中的最小可变性。
首先,简要描述我现在的域问题。
Event, Tournament, User, and Team
Teams
由 Users
组成Teams
和 Users
可以参加Tournaments
发生在 Event
Events
由 Users
组成和 Tournaments
Teams
的分数、统计数据和排名和 Users
谁在竞争Tournaments
和 Events
将是一大特色。 鉴于对问题的这种描述,我对该领域的最初想法是创建对象,其中双向、循环关系是常态——类似于图形的东西。我的想法是,能够访问任何给定对象的所有关联对象将为我提供最简单的方法来为我的数据编程 View 以及操作它。
case class User(
email: String,
teams: List[TeamUser],
events: List[EventUser],
tournaments: List[TournamentUser]) {
}
case class TournamentUser(
tournament: Tournament,
user: User,
isPresent: Boolean){
}
case class Tournament(
game: Game,
event: Event,
users: List[TournamentUser],
teams: List[TournamentTeam]) {
}
然而,当我深入研究 FP 最佳实践时,我发现我的思维过程与 FP 原则不兼容。 Circular references are frowned upon并且似乎几乎是 impossibility with immutable objects.
鉴于此,我现在正在努力解决如何重构我的域以满足良好 FP 的要求,同时仍然保持域中“现实世界对象”的常识组织。
我考虑过的一些选择
所以我正在努力改变我的实现或我的原始模型来实现我认为我需要的耦合,但是对于 Scala 来说是“正确的方式”。我该如何解决这个问题?
TL;博士 -- 当域的核心似乎要求双向访问和可变性时,如何使用良好的 FP 实践对域进行建模?
最佳答案
假设您的域模型由数据库支持,在您上面突出显示的情况下,我将使您的 User 类的“团队”、“事件”和“锦标赛”属性定义为从数据库中检索适当的对象(您如果您担心过多的数据库调用,可以实现缓存策略)。它可能看起来像:
case class User(email: String)) {
def teams = TeamService.getAllTeams.filter( { t => t.users.contains(this) } )
//similar for events and tournaments
}
另一种说法可能是你的循环依赖有一个单一的“权威”方向,而另一个方向的引用是由此计算的。这样,例如,当您将用户添加到锦标赛时,您的函数只需返回一个新的锦标赛对象(带有添加的用户),而不是一个新的锦标赛对象和一个新的用户对象。此外,Tournament 可以简单地包含一个 User/Boolean 元组列表,而不是显式地对 TournamentUser 链接表进行建模。
另一种选择可能是使用 Lenses修改您的域模型,但我还没有在这种情况下实现它们。也许在 FP 方面有更多经验的人可以在这里谈谈他们的适用性。
关于scala - 重构具有可变性和循环依赖项的域模型,以便为具有良好 FP 实践的 Scala 工作?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24677719/