Skip to content

Commit 4cecbab

Browse files
committed
refactor(cache): abstraction
1 parent 6a6beef commit 4cecbab

9 files changed

Lines changed: 43 additions & 28 deletions

File tree

src/main/scala/net/yoshinorin/qualtet/Modules.scala

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import org.typelevel.log4cats.slf4j.Slf4jFactory as Log4CatsSlf4jFactory
1111
import org.typelevel.otel4s.trace.Tracer
1212
import com.github.benmanes.caffeine.cache.{Cache as CaffeineCache, Caffeine}
1313
import net.yoshinorin.qualtet.auth.{AuthService, Jwt, KeyPair}
14-
import net.yoshinorin.qualtet.cache.CacheModule
14+
import net.yoshinorin.qualtet.cache.{CacheRepository, InMemoryCache}
1515
import net.yoshinorin.qualtet.config.ApplicationConfig
1616
import net.yoshinorin.qualtet.domains.{ArticlesPagination, FeedsPagination, Limit, Page, PaginationOps, PaginationRequestModel, TagsPagination}
1717
import net.yoshinorin.qualtet.domains.archives.{ArchiveRepository, ArchiveRepositoryAdapter, ArchiveService}
@@ -98,7 +98,7 @@ class Modules(tx: Transactor[IO], maybeTracer: Option[Tracer[IO]] = None) {
9898
val contentTypeRepositoryAdapter: ContentTypeRepositoryAdapter[ConnectionIO] = new ContentTypeRepositoryAdapter(contentTypeRepository)
9999
val contentTypeCaffeinCache: CaffeineCache[String, ContentType] =
100100
Caffeine.newBuilder().expireAfterAccess(config.cache.contentType, TimeUnit.SECONDS).build[String, ContentType]
101-
val contentTypeCache: CacheModule[IO, String, ContentType] = new CacheModule[IO, String, ContentType](contentTypeCaffeinCache)
101+
val contentTypeCache: CacheRepository[IO, String, ContentType] = new InMemoryCache[IO, String, ContentType](contentTypeCaffeinCache)
102102
val contentTypeService = new ContentTypeService[IO, ConnectionIO](contentTypeRepositoryAdapter, contentTypeCache)
103103

104104
val robotsRepository: RobotsRepository[ConnectionIO] = summon[RobotsRepository[ConnectionIO]]
@@ -113,7 +113,7 @@ class Modules(tx: Transactor[IO], maybeTracer: Option[Tracer[IO]] = None) {
113113
val tagRepository: TagRepository[ConnectionIO] = summon[TagRepository[ConnectionIO]]
114114
val tagsCaffeinCache: CaffeineCache[String, Seq[TagResponseModel]] =
115115
Caffeine.newBuilder().expireAfterAccess(config.cache.tags, TimeUnit.SECONDS).build[String, Seq[TagResponseModel]]
116-
val tagsCache: CacheModule[IO, String, Seq[TagResponseModel]] = new CacheModule[IO, String, Seq[TagResponseModel]](tagsCaffeinCache)
116+
val tagsCache: CacheRepository[IO, String, Seq[TagResponseModel]] = new InMemoryCache[IO, String, Seq[TagResponseModel]](tagsCaffeinCache)
117117
val tagRepositoryAdapter: TagRepositoryAdapter[ConnectionIO] = new TagRepositoryAdapter[ConnectionIO](tagRepository)
118118
val tagService = new TagService[IO, ConnectionIO](tagRepositoryAdapter, tagsCache, contentTaggingRepositoryAdapter)
119119

@@ -159,14 +159,14 @@ class Modules(tx: Transactor[IO], maybeTracer: Option[Tracer[IO]] = None) {
159159
val sitemapRepository: SitemapsRepository[ConnectionIO] = summon[SitemapsRepository[ConnectionIO]]
160160
val sitemapCaffeinCache: CaffeineCache[String, Seq[Url]] =
161161
Caffeine.newBuilder().expireAfterAccess(config.cache.sitemap, TimeUnit.SECONDS).build[String, Seq[Url]]
162-
val sitemapCache: CacheModule[IO, String, Seq[Url]] = new CacheModule[IO, String, Seq[Url]](sitemapCaffeinCache)
162+
val sitemapCache: CacheRepository[IO, String, Seq[Url]] = new InMemoryCache[IO, String, Seq[Url]](sitemapCaffeinCache)
163163
val sitemapRepositoryAdapter: SitemapRepositoryAdapter[ConnectionIO] = new SitemapRepositoryAdapter[ConnectionIO](sitemapRepository)
164164
val sitemapService = new SitemapService[IO, ConnectionIO](sitemapRepositoryAdapter, sitemapCache)
165165

166166
val feedsPaginationOps = summon[PaginationOps[FeedsPagination]]
167167
val feedCaffeinCache: CaffeineCache[String, ArticleWithCountResponseModel] =
168168
Caffeine.newBuilder().expireAfterAccess(config.cache.feed, TimeUnit.SECONDS).build[String, ArticleWithCountResponseModel]
169-
val feedCache: CacheModule[IO, String, ArticleWithCountResponseModel] = new CacheModule[IO, String, ArticleWithCountResponseModel](feedCaffeinCache)
169+
val feedCache: CacheRepository[IO, String, ArticleWithCountResponseModel] = new InMemoryCache[IO, String, ArticleWithCountResponseModel](feedCaffeinCache)
170170
val feedService = new FeedService[IO, ConnectionIO](
171171
feedsPaginationOps.make(PaginationRequestModel(Option(Page(1)), Option(Limit(config.feed.limit)), None)),
172172
feedCache,
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package net.yoshinorin.qualtet.cache
2+
3+
import cats.Monad
4+
5+
trait CacheRepository[F[_]: Monad, T1, T2] {
6+
7+
def get(k: T1): F[Option[T2]]
8+
9+
def put(k: T1, v: T2): F[Unit]
10+
11+
def put(k: T1, v: Option[T2]): F[Unit]
12+
13+
def invalidate(): F[Unit]
14+
15+
}

src/main/scala/net/yoshinorin/qualtet/cache/CacheModule.scala renamed to src/main/scala/net/yoshinorin/qualtet/cache/InMemoryCache.scala

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,24 @@ package net.yoshinorin.qualtet.cache
33
import cats.Monad
44
import com.github.benmanes.caffeine.cache.Cache as CaffeineCache
55

6-
class CacheModule[F[_]: Monad, T1, T2](caffeineCache: CaffeineCache[T1, T2]) {
6+
class InMemoryCache[F[_]: Monad, T1, T2](caffeineCache: CaffeineCache[T1, T2]) extends CacheRepository[F, T1, T2] {
77

8-
def get(k: T1): F[Option[T2]] = {
8+
override def get(k: T1): F[Option[T2]] = {
99
Monad[F].pure(Option(caffeineCache.getIfPresent(k)))
1010
}
1111

12-
def put(k: T1, v: T2): F[Unit] = {
12+
override def put(k: T1, v: T2): F[Unit] = {
1313
Monad[F].pure(caffeineCache.put(k, v))
1414
}
1515

16-
def put(k: T1, v: Option[T2]): F[Unit] = {
16+
override def put(k: T1, v: Option[T2]): F[Unit] = {
1717
v match {
1818
case Some(x: T2) => Monad[F].pure(caffeineCache.put(k, x))
1919
case _ => Monad[F].pure(()) // Nothing to do
2020
}
2121
}
2222

23-
def invalidate(): F[Unit] = {
23+
override def invalidate(): F[Unit] = {
2424
Monad[F].pure(caffeineCache.invalidateAll())
2525
}
2626

src/main/scala/net/yoshinorin/qualtet/domains/contentTypes/ContentTypeService.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package net.yoshinorin.qualtet.domains.contentTypes
33
import cats.Monad
44
import cats.implicits.*
55
import org.typelevel.log4cats.{LoggerFactory as Log4CatsLoggerFactory, SelfAwareStructuredLogger}
6-
import net.yoshinorin.qualtet.cache.CacheModule
6+
import net.yoshinorin.qualtet.cache.CacheRepository
77
import net.yoshinorin.qualtet.domains.errors.{DomainError, UnexpectedException}
88
import net.yoshinorin.qualtet.domains.Cacheable
99
import net.yoshinorin.qualtet.infrastructure.db.Executer
@@ -13,7 +13,7 @@ import scala.annotation.nowarn
1313

1414
class ContentTypeService[F[_]: Monad, G[_]: Monad @nowarn](
1515
contentTypeRepositoryAdapter: ContentTypeRepositoryAdapter[G],
16-
cache: CacheModule[F, String, ContentType]
16+
cache: CacheRepository[F, String, ContentType]
1717
)(using executer: Executer[F, G], loggerFactory: Log4CatsLoggerFactory[F])
1818
extends Cacheable[F] {
1919

src/main/scala/net/yoshinorin/qualtet/domains/feeds/FeedService.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package net.yoshinorin.qualtet.domains.feeds
33
import cats.Monad
44
import cats.implicits.*
55
import org.typelevel.log4cats.{LoggerFactory as Log4CatsLoggerFactory, SelfAwareStructuredLogger}
6-
import net.yoshinorin.qualtet.cache.CacheModule
6+
import net.yoshinorin.qualtet.cache.CacheRepository
77
import net.yoshinorin.qualtet.domains.articles.ArticleService
88
import net.yoshinorin.qualtet.domains.FeedsPagination
99
import net.yoshinorin.qualtet.domains.articles.ArticleWithCountResponseModel
@@ -15,7 +15,7 @@ import scala.annotation.nowarn
1515

1616
class FeedService[F[_]: Monad, G[_]: Monad @nowarn](
1717
pagination: FeedsPagination,
18-
cache: CacheModule[F, String, ArticleWithCountResponseModel],
18+
cache: CacheRepository[F, String, ArticleWithCountResponseModel],
1919
articleService: ArticleService[F, G]
2020
)(using loggerFactory: Log4CatsLoggerFactory[F])
2121
extends Cacheable[F] {

src/main/scala/net/yoshinorin/qualtet/domains/sitemaps/SitemapService.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@ package net.yoshinorin.qualtet.domains.sitemaps
22

33
import cats.Monad
44
import cats.implicits.*
5-
import net.yoshinorin.qualtet.cache.CacheModule
5+
import net.yoshinorin.qualtet.cache.CacheRepository
66
import net.yoshinorin.qualtet.infrastructure.db.Executer
77
import net.yoshinorin.qualtet.domains.Cacheable
88

99
import scala.annotation.nowarn
1010

1111
class SitemapService[F[_]: Monad, G[_]: Monad @nowarn](
1212
sitemapRepositoryAdapter: SitemapRepositoryAdapter[G],
13-
cache: CacheModule[F, String, Seq[Url]]
13+
cache: CacheRepository[F, String, Seq[Url]]
1414
)(using executer: Executer[F, G])
1515
extends Cacheable[F] {
1616

src/main/scala/net/yoshinorin/qualtet/domains/tags/TagService.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package net.yoshinorin.qualtet.domains.tags
33
import cats.Monad
44
import cats.implicits.*
55
import org.typelevel.log4cats.{LoggerFactory as Log4CatsLoggerFactory, SelfAwareStructuredLogger}
6-
import net.yoshinorin.qualtet.cache.CacheModule
6+
import net.yoshinorin.qualtet.cache.CacheRepository
77
import net.yoshinorin.qualtet.domains.contentTaggings.ContentTaggingRepositoryAdapter
88
import net.yoshinorin.qualtet.infrastructure.db.Executer
99
import net.yoshinorin.qualtet.domains.errors.{DomainError, TagNotFound}
@@ -12,7 +12,7 @@ import net.yoshinorin.qualtet.syntax.*
1212

1313
class TagService[F[_]: Monad, G[_]: Monad](
1414
tagRepositoryAdapter: TagRepositoryAdapter[G],
15-
cache: CacheModule[F, String, Seq[TagResponseModel]],
15+
cache: CacheRepository[F, String, Seq[TagResponseModel]],
1616
contentTaggingRepositoryAdapter: ContentTaggingRepositoryAdapter[G]
1717
)(using executer: Executer[F, G], loggerFactory: Log4CatsLoggerFactory[F])
1818
extends Cacheable[F] {

src/test/scala/net/yoshinorin/qualtet/cache/CacheModuleSpec.scala

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ class CacheModuleSpec extends AnyWordSpec {
1515
val contentTypeCaffeinCache: CaffeineCache[String, ContentType] =
1616
Caffeine.newBuilder().expireAfterAccess(5, TimeUnit.SECONDS).build[String, ContentType]
1717

18-
val contentTypeCache = new CacheModule[IO, String, ContentType](contentTypeCaffeinCache)
18+
val contentTypeCache = new InMemoryCache[IO, String, ContentType](contentTypeCaffeinCache)
1919
contentTypeCache.put(articleContentType.name.value, articleContentType)
2020

2121
val caffeinCache: CaffeineCache[Int, String] =
@@ -39,7 +39,7 @@ class CacheModuleSpec extends AnyWordSpec {
3939
}
4040

4141
"hit optional" in {
42-
val cache = new CacheModule[IO, Int, String](caffeinCache)
42+
val cache = new InMemoryCache[IO, Int, String](caffeinCache)
4343
(for {
4444
_ <- cache.put(1, Option("foo"))
4545
cached <- cache.get(1)
@@ -49,7 +49,7 @@ class CacheModuleSpec extends AnyWordSpec {
4949
}
5050

5151
"miss optional" in {
52-
val cache = new CacheModule[IO, Int, String](caffeinCache)
52+
val cache = new InMemoryCache[IO, Int, String](caffeinCache)
5353
(for {
5454
_ <- cache.put(2, None)
5555
cached <- cache.get(2)
@@ -59,7 +59,7 @@ class CacheModuleSpec extends AnyWordSpec {
5959
}
6060

6161
"miss after expire" in {
62-
val cache = new CacheModule[IO, Int, String](caffeinCache)
62+
val cache = new InMemoryCache[IO, Int, String](caffeinCache)
6363
(for {
6464
_ <- cache.put(3, "foo")
6565
cached <- cache.get(1)
@@ -72,7 +72,7 @@ class CacheModuleSpec extends AnyWordSpec {
7272
}
7373

7474
"flush" in {
75-
val cache = new CacheModule[IO, Int, String](caffeinCache)
75+
val cache = new InMemoryCache[IO, Int, String](caffeinCache)
7676
(for {
7777
_ <- cache.put(4, "bar")
7878
_ <- cache.put(5, "baz")

src/test/scala/net/yoshinorin/qualtet/fixture/Fixture.scala

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import com.github.plokhotnyuk.jsoniter_scala.core.JsonValueCodec
1616
import net.yoshinorin.qualtet.config.ApplicationConfig
1717
import net.yoshinorin.qualtet.http.AuthProvider
1818
import net.yoshinorin.qualtet.http.CorsProvider
19-
import net.yoshinorin.qualtet.cache.CacheModule
19+
import net.yoshinorin.qualtet.cache.{CacheRepository, InMemoryCache}
2020
import net.yoshinorin.qualtet.domains.contents.ContentPath
2121
import net.yoshinorin.qualtet.domains.articles.*
2222
import net.yoshinorin.qualtet.domains.authors.*
@@ -113,19 +113,19 @@ object Fixture {
113113
// TODO: from config for cache options
114114
val contentTypeCaffeinCache: CaffeineCache[String, ContentType] =
115115
Caffeine.newBuilder().expireAfterAccess(5, TimeUnit.SECONDS).build[String, ContentType]
116-
val contentTypeCache = new CacheModule[IO, String, ContentType](contentTypeCaffeinCache)
116+
val contentTypeCache = new InMemoryCache[IO, String, ContentType](contentTypeCaffeinCache)
117117
val contentTypeService = new ContentTypeService(modules.contentTypeRepositoryAdapter, contentTypeCache)
118118

119119
val sitemapRepository: SitemapsRepository[ConnectionIO] = summon[SitemapsRepository[ConnectionIO]]
120120
val sitemapCaffeinCache: CaffeineCache[String, Seq[Url]] =
121121
Caffeine.newBuilder().expireAfterAccess(5, TimeUnit.SECONDS).build[String, Seq[Url]]
122-
val sitemapCache = new CacheModule[IO, String, Seq[Url]](sitemapCaffeinCache)
122+
val sitemapCache = new InMemoryCache[IO, String, Seq[Url]](sitemapCaffeinCache)
123123
val sitemapRepositoryAdapter: SitemapRepositoryAdapter[ConnectionIO] = new SitemapRepositoryAdapter[ConnectionIO](sitemapRepository)
124124
val sitemapService = new SitemapService(sitemapRepositoryAdapter, sitemapCache)
125125

126126
val feedCaffeinCache: CaffeineCache[String, ArticleWithCountResponseModel] =
127127
Caffeine.newBuilder().expireAfterAccess(5, TimeUnit.SECONDS).build[String, ArticleWithCountResponseModel]
128-
val feedCache: CacheModule[IO, String, ArticleWithCountResponseModel] = new CacheModule[IO, String, ArticleWithCountResponseModel](feedCaffeinCache)
128+
val feedCache: CacheRepository[IO, String, ArticleWithCountResponseModel] = new InMemoryCache[IO, String, ArticleWithCountResponseModel](feedCaffeinCache)
129129
val feedService = new FeedService(
130130
summon[PaginationOps[FeedsPagination]].make(PaginationRequestModel(Option(Page(1)), Option(Limit(5)), None)),
131131
feedCache,
@@ -134,7 +134,7 @@ object Fixture {
134134

135135
val tagsCaffeinCache: CaffeineCache[String, Seq[TagResponseModel]] =
136136
Caffeine.newBuilder().expireAfterAccess(5, TimeUnit.SECONDS).build[String, Seq[TagResponseModel]]
137-
val tagsCache: CacheModule[IO, String, Seq[TagResponseModel]] = new CacheModule[IO, String, Seq[TagResponseModel]](tagsCaffeinCache)
137+
val tagsCache: CacheRepository[IO, String, Seq[TagResponseModel]] = new InMemoryCache[IO, String, Seq[TagResponseModel]](tagsCaffeinCache)
138138
val tagService = new TagService(tagRepositoryAdapter, tagsCache, contentTaggingRepositoryAdapter)
139139

140140
val authProvider = new AuthProvider[IO, ConnectionIO](modules.authService)

0 commit comments

Comments
 (0)