The first problem I was trying to solve was how to define an API that could either be synchronous or asynchronous. This is what I defined
trait Leaderboard {
type Response[A]
. . .
def getCount: Response[Int]
. . .
}
trait LeaderboardSync extends Leaderboard {
type Response[A] = A
}
trait LeaderboardAsync extends Leaderboard {
type Response[A] = Future[A]
}
class SynchronizedLeaderboard(. . .) extends LeaderboardSync {
override def getCount: Int = consecutiveLeaderboard.getCount
}
class LeaderboardActor(. . .) extends LeaderboardAsync {
override def getCount: Future[Int] =
selfActorReference ? (actorRef ⇒ GetCount(actorRef))
}
So I now have multiple implementations, some synchronous, some asynchronous. This seems to work well in practice. I like this design pattern that someone else gave me.
Where I am currently exploring is what is the best way to process results from these APIs when multiple implementations are in play.
One pattern I follow is
def handle[I,O](input: Any, output: I ⇒ O): Future[O] = {
input match {
case future: Future[I] ⇒ // This needs to come first
future.map(value => output(value))
case value: I ⇒
Future.successful(output(value))
}
}
For example
handle[scorekeeping.Score,MemberStatusResponse](
leaderboard.update(updateMode, memberIdentifier, bigIntScore),
score ⇒ MemberStatusResponse(leaderboardUrlId, memberUrlId, Some(Score(score))))
So I always return a Future, some already complete, some not. Alternately, I was thinking of returning Either instead of Future, Either[A,Future[A]], but I don’t have a sense of a best practice yet.
In the more abstract sense, what is the better way of handling Interfaces with polymorphic results, where synchronous/asynchronous is just a narrow example?
I’m looking for some discussion on what might be the best design and implementation practices.