Ho seguito il principio di progettazione dal libro Modellazione funzionale e reattiva.Come implementare la memorizzazione nella cache con Kleisli
Quindi tutti i metodi restituiscono Kleisli
.
La domanda è come è possibile aggiungere una cache aggiornabile su questi servizi.
Ecco la mia attuale implementazione, c'è un modo migliore (combinatori esistenti, approccio più funzionale, ...)?
import scala.concurrent.duration.Duration
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.{Await, Future}
import scalaz.Kleisli
trait Repository {
def all : Future[Seq[String]]
def replaceAll(l: Seq[String]) : Future[Unit]
}
trait Service {
def all = Kleisli[Future, Repository, Seq[String]] { _.all }
def replaceAll(l: Seq[String]) = Kleisli[Future, Repository, Unit] { _.replaceAll(l) }
}
trait CacheService extends Service {
var cache : Seq[String] = Seq.empty[String]
override def all = Kleisli[Future, Repository, Seq[String]] { repo: Repository =>
if (cache.isEmpty) {
val fcache = repo.all
fcache.foreach(cache = _)
fcache
}
else
Future.successful(cache)
}
override def replaceAll(l: Seq[String]) = Kleisli[Future, Repository, Unit] { repo: Repository =>
cache = l
repo.replaceAll(l)
}
}
object CacheTest extends App {
val repo = new Repository {
override def replaceAll(l: Seq[String]): Future[Unit] = Future.successful()
override def all: Future[Seq[String]] = Future.successful(Seq("1","2","3"))
}
val service = new CacheService {}
println(Await.result(service.all(repo), Duration.Inf))
Await.result(service.replaceAll(List("a"))(repo), Duration.Inf)
println(Await.result(service.all(repo), Duration.Inf))
}
[update] Per quanto riguarda il commento di @timotyperigo, ho attuazione del caching a livello di repository
class CachedTipRepository(val self:TipRepository) extends TipRepository {
var cache: Seq[Tip] = Seq.empty[Tip]
override def all: Future[Seq[Tip]] = …
override def replace(tips: String): Unit = …
}
Sono ancora interessato per il feedback per migliorare il design.
Solo un pensiero ... mi sembra che qualcosa come il caching in realtà non sia un comportamento di dominio (cioè qualcosa che dovrebbe essere parte del tuo servizio), ma forse piuttosto una proprietà dell'implementazione del repository. Il tuo servizio conterrà quindi solo le azioni necessarie per eseguire il comportamento richiesto, ma (se lo desideri) la tua applicazione potrebbe scegliere tra caching e repository non in cache. Nell'implementazione del repository, è possibile utilizzare qualcosa come la monade di stato per un approccio più funzionale alla memorizzazione nella cache. –