Я следовал принципу дизайна из книги Функциональное и реактивное моделирование.Как реализовать кеширование с помощью Kleisli
Таким образом, все сервис методы возвращаются Kleisli
.
Вопрос в том, как я могу добавить обновляемый кэш по этим услугам.
Вот моя текущая реализация, есть ли лучший способ (существующие комбинаторы, более функциональный подход, ...)?
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))
}
[обновление] Что касается замечания о @timotyperigo, я реализует кэширование на уровне хранилища
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 = …
}
Я все еще заинтересован в обратной связи, чтобы улучшить дизайн.
Просто мысль ... мне кажется, что что-то вроде кэширования действительно не является поведением домена (то есть, что-то, что должно быть частью вашей службы), но, возможно, скорее свойство реализации Репозитория. Тогда ваша служба будет содержать только действия, необходимые для выполнения требуемого поведения, но (если вы захотите), ваше приложение может выбирать между кэшированием и не кэшированием репозиториев. В рамках реализации репозитория вы можете использовать нечто вроде государственной монады для более функционального подхода к кешированию. –