8

我来自 OO 背景(C#、javascript),Scala 是我第一次涉足 FP。

由于我的背景,我在实现一个非常适合我的领域问题并且符合 FP 的良好实践(例如代码中的最小可变性)的领域模型时遇到了困难。

首先,简要描述一下我现在的域问题。

  • 主要领域对象是:Event, Tournament, User, and Team
  • TeamsUsers
  • 两者Teams都可以Users参加TournamentsEvent
  • Events包括UsersTournaments
  • 得分、统计数据和排名,Teams以及Users谁竞争TournamentsEvents将是一个主要特征。

鉴于对问题的描述,我对域的最初想法是创建对象,其中双向循环关系是常态——类似于图。我的思路是,能够访问任何给定对象的所有关联对象将为我提供最简单的数据视图编程和操作途径。

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 原则不兼容。循环引用不受欢迎,对于不可变对象似乎几乎是不可能的。

鉴于此,我现在正在努力解决如何重构我的域以满足良好 FP 的要求,同时仍然保持域中“现实世界对象”的常识组织。

我考虑过的一些选项

  • 使用惰性 val 和按名称引用——我对此的疑虑是,一旦域变得不平凡,这似乎变得难以管理
  • 改用单向关系——虽然我被迫将一些域对象降级为只能通过其他对象访问的第二类对象,但使用这种方法。我会如何选择?它们对我来说似乎都同样重要。另外,这将需要构建“反对谷物”的查询,以获取第二类对象的简单列表。
  • 使用间接并存储关系标识符列表——这消除了周期性依赖,但随后会产生更多复杂性,因为我必须编写额外的业务逻辑来模拟关系更新并额外访问数据库以获得任何关系。

所以我正在努力改变我的实现或我的原始模型以实现我认为我需要的耦合,但以“正确的方式”用于 Scala。我该如何解决这个问题?

TL;DR——当域似乎需要双向访问和核心可变性时,我如何使用良好的 FP 实践对域进行建模?

4

1 回答 1

2

假设您的域模型由数据库支持,在您上面突出显示的情况下,我将创建 User 类定义的“团队”、“事件”和“锦标赛”属性,以便从数据库中检索适当的对象(您如果您担心过多的数据库调用,可以实施缓存策略)。它可能看起来像:

case class User(email: String)) {
    def teams = TeamService.getAllTeams.filter( { t => t.users.contains(this) } )
    //similar for events and tournaments
}

另一种说法可能是你的循环依赖有一个单一的“权威”方向,而另一个方向的引用是从这个方向计算的。这样,例如,当您将用户添加到锦标赛时,您的函数只需返回一个新的锦标赛对象(与添加的用户一起),而不是一个新的锦标赛对象和一个新的用户对象。此外,Tournament 可以简单地包含用户/布尔元组列表,而不是显式地建模 TournamentUser 链接表。

另一种选择可能是使用Lenses来修改您的域模型,但我还没有在这种情况下实现它们。也许在 FP 方面有更多经验的人可以在这里谈谈他们的适用性。

于 2014-08-03T03:30:07.967 回答