重构具有易变性和周期依赖性的域模型,以便使用良好的FP实践为Scala工作?

重构具有易变性和周期依赖性的域模型,以便使用良好的FP实践为Scala工作?,scala,functional-programming,immutability,domain-model,cyclic-reference,Scala,Functional Programming,Immutability,Domain Model,Cyclic Reference,我来自OO背景(C#,javascript),Scala是我第一次涉足FP 由于我的背景,我在实现一个域模型时遇到了困难,该模型很好地解决了我的域问题,并且符合FP的良好实践,例如代码中的最小可变性 首先,简要描述我的领域问题 主要的域对象是:事件、锦标赛、用户和团队 团队由用户组成 团队和用户都可以参加在活动 事件由用户和锦标赛 在锦标赛和事件中竞争的团队和用户的分数、统计数据和排名将是一项主要功能 鉴于对这个问题的描述,我对这个领域的最初想法是创建对象,其中双向循环关系是规范——类似于图

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

由于我的背景,我在实现一个域模型时遇到了困难,该模型很好地解决了我的域问题,并且符合FP的良好实践,例如代码中的最小可变性

首先,简要描述我的领域问题

  • 主要的域对象是:
    事件、锦标赛、用户和团队
  • 团队由
    用户组成
  • 团队
    用户
    都可以参加在
    活动
  • 事件
    用户
    锦标赛
  • 锦标赛
    事件
    中竞争的
    团队
    用户
    的分数、统计数据和排名将是一项主要功能
鉴于对这个问题的描述,我对这个领域的最初想法是创建对象,其中双向循环关系是规范——类似于图形。我的思路是,能够访问任何给定对象的所有关联对象将为我的数据视图编程和操作提供最简单的路径

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的要求,同时仍然保持域中“真实世界对象”的常识性组织

我考虑过一些选择

  • ——我对此感到不安的是,一旦这个领域变得非同寻常,它似乎就变得难以管理了
  • --使用此方法,尽管我被迫将一些域对象降级为只能通过其他对象访问的二级对象。我将如何选择?它们对我来说似乎都同等重要。此外,这将需要“根据粒度”构建查询,以获得第二类对象的简单列表
  • 使用间接寻址并存储关系的标识符列表——这会消除周期性依赖,但会增加复杂性,因为我必须编写额外的业务逻辑来模拟关系更新,并额外访问数据库以获取任何关系
因此,我正在努力改变我的实现或我的原始模型,以实现我认为需要的耦合,但要以Scala的“正确方式”。如何解决这个问题?


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

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

case class User(email: String)) {
    def teams = TeamService.getAllTeams.filter( { t => t.users.contains(this) } )
    //similar for events and tournaments
}
另一种说法可能是,您的循环依赖项有一个“权威”方向,而另一个方向上的引用是根据这个方向计算的。例如,通过这种方式,当您将用户添加到锦标赛中时,您的函数只需返回一个新的锦标赛对象(与添加的用户一起),而不是一个新的锦标赛对象和一个新的用户对象。此外,与显式建模TournamentUser链接表不同,Tournament可以只包含用户/布尔元组列表

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