I come from an OO background(C#, javascript) and Scala is my first foray into FP.
Because of my background I am having trouble realizing a domain model that fits my domain problem well and also complies with good practices for FP such as minimal mutability in code.
First, a short description of my domain problem as it is now.
- Main domain objects are:
Event, Tournament, User, and Team
Teams
are made up ofUsers
- Both
Teams
andUsers
can attendTournaments
which take place at anEvent
Events
consist ofUsers
andTournaments
- Scores, stats, and rankings for
Teams
andUsers
who compete acrossTournaments
andEvents
will be a major feature.
Given this description of the problem my initial idea for the domain is create objects where bidirectional, cyclic relationships are the norm -- something akin to a graph. My line of thinking is that being able to access all associated objects for any given given object will offer me the easiest path for programming views for my data, as well as manipulating it.
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]) {
}
However as I have dived further into FP best practices I have found that my thought process is incompatible with FP principles. Circular references are frowned upon and seem to be almost an impossibility with immutable objects.
Given this, I am now struggling with how to refactor my domain to meet the requirements for good FP while still maintaining a common sense organization of the "real world objects" in the domain.
Some options I've considered
- Use lazy val and by-name references -- My qualm with this is that seems to become unmanageable once the domain becomes non-trivial
- Use uni-directional relationships instead -- With this method though I am forced to relegate some domain objects as second class objects which can only be accessed through other objects. How would I choose? They all seem equally important to me. Plus this would require building queries "against the grain" just to get a simple list of the second class objects.
- Use indirection and store a list of identifiers for relationships -- This removes cyclical dependencies but then creates more complexity because I would have to write extra business logic to emulate relationship updates and make extra trips to the DB to get any relationship.
So I'm struggling with how to alter either my implementation or my original model to achieve the coupling I think I need but in "the right way" for Scala. How do I approach this problem?
TL;DR -- How do I model a domain using good FP practices when the domain seems to call for bidirectional access and mutability at its core?