8

私は OO のバックグラウンド (C#、javascript) から来ており、Scala は FP への最初の進出です。

私のバックグラウンドのために、ドメインの問題にうまく適合し、コードの最小限の可変性などの FP の優れたプラクティスにも準拠するドメイン モデルを実現するのに苦労しています。

最初に、現在の私のドメインの問題について簡単に説明します。

  • 主なドメイン オブジェクトは次のとおりです。Event, Tournament, User, and Team
  • Teamsで構成されていますUsers
  • との両方TeamsUsers参加できますTournamentsEvent
  • EventsUsersとからなるTournaments
  • とのスコア、統計、およびランキングは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 の要件を満たすようにドメインをリファクタリングする方法に苦労しています。

私が検討したいくつかのオプション

  • lazy val と by-name 参照を使用してください-- これに関する私の不満は、ドメインが自明ではなくなると、管理不能になるように思われることです
  • 代わりに一方向の関係を使用してください-- この方法では、一部のドメイン オブジェクトを、他のオブジェクトを介してのみアクセスできる 2 番目のクラス オブジェクトとして強制的に格下げする必要があります。どのように選択しますか?それらはすべて私にとって等しく重要に思えます。さらに、これには、2 番目のクラス オブジェクトの単純なリストを取得するためだけに、「粒度に反して」クエリを作成する必要があります。
  • インダイレクションを使用し、リレーションシップの識別子のリストを保存します。これにより、循環的な依存関係が削除されますが、リレーションシップの更新をエミュレートするために追加のビジネス ロジックを記述し、関係を取得するために DB に余分なトリップを行う必要があるため、複雑さが増します。

そのため、実装または元のモデルのいずれかを変更して、必要と思われる結合を達成する方法に苦労していますが、Scala の「正しい方法」で。どうすればこの問題にアプローチできますか?

TL;DR -- ドメインがそのコアで双方向アクセスと可変性を要求しているように見える場合、適切な FP プラクティスを使用してドメインをモデル化するにはどうすればよいですか?

4

1 に答える 1

2

ドメイン モデルがデータベースに支えられていると仮定すると、上記で強調したケースでは、データベースから適切なオブジェクトを取得する User クラス定義の「チーム」、「イベント」、および「トーナメント」プロパティを作成します (過剰な db 呼び出しが心配な場合は、キャッシュ戦略を実装できます)。次のようになります。

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

別の言い方をすれば、循環依存関係には単一の「信頼できる」方向があり、他の方向の参照はこれから計算されるということです。この方法では、たとえば、ユーザーをトーナメントに追加する場合、関数は、新しいトーナメント オブジェクトと新しいユーザー オブジェクトではなく、(追加されたユーザーを含む) 新しいトーナメント オブジェクトを返すだけで済みます。また、TournamentUser リンク テーブルを明示的にモデル化するのではなく、Tournament に単純に User/Boolean タプルのリストを含めることもできます。

もう 1 つのオプションは、レンズを使用してドメイン モデルを変更することかもしれませんが、このような状況ではレンズを実装していません。FP の経験が豊富な人なら、ここでその適用可能性について話すことができるかもしれません。

于 2014-08-03T03:30:07.967 に答える