scala - 重构具有可变性和循环依赖项的域模型,以便为具有良好 FP 实践的 Scala 工作?

标签 scala functional-programming immutability domain-model cyclic-reference

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

由于我的背景,我无法实现一个非常适合我的域问题并且也符合 FP 的良好实践的域模型,例如代码中的最小可变性。

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

  • 主要域对象是:Event, Tournament, User, and Team
  • TeamsUsers 组成
  • 两者 TeamsUsers可以参加Tournaments发生在 Event
  • EventsUsers 组成和 Tournaments
  • Teams 的分数、统计数据和排名和 Users谁在竞争TournamentsEvents将是一大特色。

  • 鉴于对问题的这种描述,我对该领域的最初想法是创建对象,其中双向、循环关系是常态——类似于图形的东西。我的想法是,能够访问任何给定对象的所有关联对象将为我提供最简单的方法来为我的数据编程 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 的要求,同时仍然保持域中“现实世界对象”的常识组织。

    我考虑过的一些选择
  • Use lazy val and by-name references -- 我对此的疑虑是,一旦域变得不重要,这似乎变得难以管理
  • Use uni-directional relationships instead -- 使用这种方法,虽然我被迫将某些域对象降级为只能通过其他对象访问的第二类对象。我会如何选择?他们对我来说似乎都同样重要。此外,这将需要构建“反对 Cereal ”的查询,以获取第二类对象的简单列表。
  • 使用间接并存储关系的标识符列表 -- 这消除了循环依赖,但随后会产生更多的复杂性,因为我必须编写额外的业务逻辑来模拟关系更新并额外访问数据库以获得任何关系。

  • 所以我正在努力改变我的实现或我的原始模型来实现我认为我需要的耦合,但是对于 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/

    相关文章:

    scala - 如何在 scala 枚举值中添加点

    Scala 惰性并行集合(可能吗?)

    java - Java中不可变对象(immutable对象)的缺点?

    java - Guava 的 Collections.unmodifiableSet() 和 ImmutableSet 有什么区别?

    scala - 在 sbt 项目中使用 Maven Central 的处理库时出现奇怪的错误

    scala - 匹配 Akka 中的值类

    R:多个连续 gsub 的函数式方法

    haskell - 为什么 Haskell 中的联积类型没有简单的语法?

    python - 返回多个对象并将它们用作参数

    C# 从变量中删除空格