I'm having trouble with what the best/idiomatic ap...
# getting-started
c
I'm having trouble with what the best/idiomatic approach would be for flattening out models that come from multiple network responeses. For example. I currently have a TeamsDTO and PlayerDTO. And an api call of
getTeams()
and
getPlayersFromTeamId(teamId)
So essentially, I want to flatten TeamsDTO and PlayerDTO into a "domain" Teams object which contains the list of Players inside of it.
data class Team(val name: String, val players: List<Player>)
Now I know I can probably get fancy with flows and stuff like that, but in terms of modelling the data, I think it kinda gets tricky because I'm using data classes and so I can't just update these things easily. I'll put my current impl in the thread, but appreciate any pointers. thanks
Copy code
fun createDomainTeamsObject() : List<Team>{
val finalList = mutableListOf<Team>()

  val teams = api.getTeams()
  teams.when{
   Success -> {
     teams.forEach { team ->
     finalList.add(Team(team.name, players = null))
     val players = api.getPlayersFromTeamId(team.id)
         players.when {
         Success -> {
            //How should I now add Players to the team?
          }
         }
     }
   }
  } 
}
z
Instead of adding Team in finalList before fetching players add it after you got players from api
c
hm yeah. i didn't think of that. so basically wait till i have everything to construct a proper team. then just add all teams to the list.
thanks. that makes sense
For what it's worth. I basically have this but like 4 levels deep and its just incredibly confusing lol Teams, with List<Players>, Players have List<Identification> and Identification has a List<Stamps> so it's just a bunch of nested calls, and me jus trying to sort it all out into something sensible to use as a data structure. lol
s
your
Copy code
{teams/players}.when {
  Success -> {
code is pseudo code isn't it? Or is
when
a custom extension function? I'm trying to wrap my head around what's happening here. My general advise is to use `map`/`mapNotNull.` Let's assume
api.getTeams()
returns a nullable list of
TeamDTO
(it's null, if the request goes wrong) and
api.getPlayersFromTeamId(team.id)
returns a nullable list of
PlayerDTO
.
Copy code
fun createDomainTeamsObject() : List<Team> =
  api.getTeams()?.let { teamDTOs: List<TeamDTO> ->
    teamDTOs.map { teamDTO ->
      val playerDTOs: List<PlayerDTO> = api.getPlayersFromTeamId(teamDTO.id) ?: emptyList()
      val players: List<Player> = playerDTOs.map { playerDTO ->
        Player(...)
      }
      Team(teamDTO.name, players)
    }
  } ?: emptyList()
This code assumes you'll always get a valid Team/Player from a TeamDTO/PlayerDTO. If that's not the case because sometimes the dto might not have all the information required, use
mapNotNull
instead.
c
teams and players are network responses, so I need to do my work in the Success -> branch and not the Failure branch.
I will try using map, maybe it'll cleanup my code. its just really messy when this is nested like 3 or 4 times. i.e. Teams, with List<Players>, Players have List<Identification> and Identification has a List<Stamps> like. it really looks unreadable. lmao
f
you seem to be doing the flattening in the domain layer, this would work better if you were to do it at the repository layer, combining the responses from the 2 sources there
c
I have this "flattening" happening in a class called GetTeamUseCase. I don't really have a repository pattern anywhere. but aside from "where" its happening, I guess the way I'm doing the flattening doesn't seem too bad to you @Francesc?
f
it looks fine to me. I think you can make your life a bit easier if you throw on error (or encapsulate on a
Result
type of object)
Copy code
data class TeamDto(
    val id: Int,
    val name: String,
)

data class PlayerDto(
    val id: Int,
    val name: String,
)

data class Player(
    val id: Int,
    val name: String,
)

data class Team(
    val id: Int,
    val name: String,
    val players: List<Player>
)

interface RemoteDataSource {
    suspend fun getTeams(): Response<List<TeamDto>>

    suspend fun getPlayers(teamId: Int): Response<List<PlayerDto>>
}

class NetworkException : IOException()

fun <T> Response<T>.valueOrThrow():T = if (isSuccessful && body() != null) body()!! else throw NetworkException()

suspend fun getTeams(): List<Team> {
    val teams = remoteDataSource.getTeams().valueOrThrow()
    return teams.map { team ->
        Team(
            id = team.id,
            name = team.name,
            players = remoteDataSource.getPlayers(team.id).valueOrThrow().map { player ->
                Player(
                    id = player.id,
                    name = player.name,
                )
            }
        )
    }
}