Skip to content

Commit

Permalink
refactor(domain/repository): type classes
Browse files Browse the repository at this point in the history
  • Loading branch information
yoshinorin committed Feb 27, 2024
1 parent e7e7ea5 commit 8ef223d
Show file tree
Hide file tree
Showing 30 changed files with 542 additions and 488 deletions.
54 changes: 28 additions & 26 deletions src/main/scala/net/yoshinorin/qualtet/Modules.scala
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
package net.yoshinorin.qualtet

import doobie.ConnectionIO
import com.github.benmanes.caffeine.cache.{Cache => CaffeineCache, Caffeine}
import net.yoshinorin.qualtet.auth.{AuthService, Jwt, KeyPair}
import net.yoshinorin.qualtet.cache.CacheModule
import net.yoshinorin.qualtet.config.ApplicationConfig
import net.yoshinorin.qualtet.domains.archives.{ArchiveRepositoryDoobieInterpreter, ArchiveService}
import net.yoshinorin.qualtet.domains.articles.{ArticleRepositoryDoobieInterpreter, ArticleService}
import net.yoshinorin.qualtet.domains.authors.{AuthorRepositoryDoobieInterpreter, AuthorService}
import net.yoshinorin.qualtet.domains.contentTypes.{ContentType, ContentTypeRepositoryDoobieInterpreter, ContentTypeService}
import net.yoshinorin.qualtet.domains.contentTaggings.{ContentTaggingRepositoryDoobieInterpretere, ContentTaggingService}
import net.yoshinorin.qualtet.domains.contents.{ContentRepositoryDoobieInterpreter, ContentService}
import net.yoshinorin.qualtet.domains.contentSerializing.{ContentSerializingRepositoryDoobieInterpretere, ContentSerializingService}
import net.yoshinorin.qualtet.domains.externalResources.{ExternalResourceRepositoryDoobieInterpreter, ExternalResourceService}
import net.yoshinorin.qualtet.domains.robots.{RobotsRepositoryDoobieInterpreter, RobotsService}
import net.yoshinorin.qualtet.domains.search.{SearchRepositoryDoobieInterpreter, SearchService}
import net.yoshinorin.qualtet.domains.series.{SeriesRepositoryDoobieInterpreter, SeriesService}
import net.yoshinorin.qualtet.domains.sitemaps.{SitemapService, SitemapsRepositoryDoobieInterpreter, Url}
import net.yoshinorin.qualtet.domains.tags.{TagRepositoryDoobieInterpreter, TagService}
import net.yoshinorin.qualtet.domains.archives.{ArchiveRepository, ArchiveService}
import net.yoshinorin.qualtet.domains.articles.{ArticleRepository, ArticleService}
import net.yoshinorin.qualtet.domains.authors.{AuthorRepository, AuthorService}
import net.yoshinorin.qualtet.domains.contentTypes.{ContentType, ContentTypeService}
import net.yoshinorin.qualtet.domains.contentTaggings.{ContentTaggingRepository, ContentTaggingService}
import net.yoshinorin.qualtet.domains.contents.{ContentRepository, ContentService}
import net.yoshinorin.qualtet.domains.contentSerializing.{ContentSerializingRepository, ContentSerializingService}
import net.yoshinorin.qualtet.domains.contentTypes.ContentTypeRepository
import net.yoshinorin.qualtet.domains.externalResources.{ExternalResourceRepository, ExternalResourceService}
import net.yoshinorin.qualtet.domains.robots.{RobotsRepository, RobotsService}
import net.yoshinorin.qualtet.domains.search.{SearchRepository, SearchService}
import net.yoshinorin.qualtet.domains.series.{SeriesRepository, SeriesService}
import net.yoshinorin.qualtet.domains.sitemaps.{SitemapService, SitemapsRepository, Url}
import net.yoshinorin.qualtet.domains.tags.{TagRepository, TagService}
import net.yoshinorin.qualtet.auth.Signature
import net.yoshinorin.qualtet.domains.feeds.FeedService
import net.yoshinorin.qualtet.cache.CacheService
Expand All @@ -41,42 +43,42 @@ object Modules {
val signature: Signature = new net.yoshinorin.qualtet.auth.Signature("SHA256withRSA", message, keyPair)
val jwtInstance: Jwt = new Jwt(config.jwt, JwtAlgorithm.RS256, keyPair, signature)

val authorRepository: AuthorRepositoryDoobieInterpreter = new AuthorRepositoryDoobieInterpreter()
val authorRepository: AuthorRepository[ConnectionIO] = summon[AuthorRepository[ConnectionIO]]
val authorService = new AuthorService(authorRepository)

val authService = new AuthService(authorService, jwtInstance)

val contentTypeRepository: ContentTypeRepositoryDoobieInterpreter = new ContentTypeRepositoryDoobieInterpreter()
val contentTypeRepository: ContentTypeRepository[ConnectionIO] = summon[ContentTypeRepository[ConnectionIO]]
val contentTypeCaffeinCache: CaffeineCache[String, ContentType] =
Caffeine.newBuilder().expireAfterAccess(config.cache.contentType, TimeUnit.SECONDS).build[String, ContentType]
val contentTypeCache: CacheModule[String, ContentType] = new CacheModule[String, ContentType](contentTypeCaffeinCache)
val contentTypeService = new ContentTypeService(contentTypeRepository, contentTypeCache)

val robotsRepository: RobotsRepositoryDoobieInterpreter = new RobotsRepositoryDoobieInterpreter()
val robotsRepository: RobotsRepository[ConnectionIO] = summon[RobotsRepository[ConnectionIO]]
val robotsService = new RobotsService(robotsRepository)

val externalResourceRepository: ExternalResourceRepositoryDoobieInterpreter = new ExternalResourceRepositoryDoobieInterpreter()
val externalResourceRepository: ExternalResourceRepository[ConnectionIO] = summon[ExternalResourceRepository[ConnectionIO]]
val externalResourceService = new ExternalResourceService(externalResourceRepository)

val contentTaggingRepository: ContentTaggingRepositoryDoobieInterpretere = new ContentTaggingRepositoryDoobieInterpretere()
val contentTaggingRepository: ContentTaggingRepository[ConnectionIO] = summon[ContentTaggingRepository[ConnectionIO]]
val contentTaggingService = new ContentTaggingService(contentTaggingRepository)

val tagRepository: TagRepositoryDoobieInterpreter = new TagRepositoryDoobieInterpreter()
val tagRepository: TagRepository[ConnectionIO] = summon[TagRepository[ConnectionIO]]
val tagService = new TagService(tagRepository, contentTaggingService)

val searchRepository: SearchRepositoryDoobieInterpreter = new SearchRepositoryDoobieInterpreter()
val searchRepository: SearchRepository[ConnectionIO] = summon[SearchRepository[ConnectionIO]]
val searchService = new SearchService(config.search, searchRepository)

val articleRepository = new ArticleRepositoryDoobieInterpreter()
val articleRepository: ArticleRepository[ConnectionIO] = summon[ArticleRepository[ConnectionIO]]
val articleService = new ArticleService(articleRepository, contentTypeService)

val seriesRepository: SeriesRepositoryDoobieInterpreter = new SeriesRepositoryDoobieInterpreter()
val seriesRepository: SeriesRepository[ConnectionIO] = summon[SeriesRepository[ConnectionIO]]
val seriesService = new SeriesService(seriesRepository, articleService)

val contentSerializingRepository: ContentSerializingRepositoryDoobieInterpretere = new ContentSerializingRepositoryDoobieInterpretere()
val contentSerializingRepository: ContentSerializingRepository[ConnectionIO] = summon[ContentSerializingRepository[ConnectionIO]]
val contentSerializingService = new ContentSerializingService(contentSerializingRepository)

val contentRepository: ContentRepositoryDoobieInterpreter = new ContentRepositoryDoobieInterpreter()
val contentRepository: ContentRepository[ConnectionIO] = summon[ContentRepository[ConnectionIO]]
val contentService =
new ContentService(
contentRepository,
Expand All @@ -90,10 +92,10 @@ object Modules {
contentSerializingService
)

val archiveRepository: ArchiveRepositoryDoobieInterpreter = new ArchiveRepositoryDoobieInterpreter()
val archiveRepository: ArchiveRepository[ConnectionIO] = summon[ArchiveRepository[ConnectionIO]]
val archiveService = new ArchiveService(archiveRepository, contentTypeService)

val sitemapRepository: SitemapsRepositoryDoobieInterpreter = new SitemapsRepositoryDoobieInterpreter()
val sitemapRepository: SitemapsRepository[ConnectionIO] = summon[SitemapsRepository[ConnectionIO]]
val sitemapCaffeinCache: CaffeineCache[String, Seq[Url]] =
Caffeine.newBuilder().expireAfterAccess(config.cache.sitemap, TimeUnit.SECONDS).build[String, Seq[Url]]
val sitemapCache: CacheModule[String, Seq[Url]] = new CacheModule[String, Seq[Url]](sitemapCaffeinCache)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,22 @@ import net.yoshinorin.qualtet.domains.contentTypes.ContentTypeId
trait ArchiveRepository[F[_]] {
def get(contentTypeId: ContentTypeId): F[Seq[ResponseArchive]]
}

object ArchiveRepository {

import doobie.Read
import doobie.ConnectionIO
import net.yoshinorin.qualtet.domains.contents.Path

given ArchiveRepository: ArchiveRepository[ConnectionIO] = {
new ArchiveRepository[ConnectionIO] {
given responseArchivesRead: Read[ResponseArchive] =
Read[(String, String, Long)].map { case (path, title, publishedAt) =>
ResponseArchive(Path(path), title, publishedAt)
}

override def get(contentTypeId: ContentTypeId): ConnectionIO[Seq[ResponseArchive]] = ArchiveQuery.get(contentTypeId).to[Seq]
}
}

}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,31 @@ trait ArticleRepository[F[_]] {
def findByTagNameWithCount(contentTypeId: ContentTypeId, tagName: TagName, sqlParams: SqlParams): F[Seq[(Int, ResponseArticle)]]
def findBySeriesNameWithCount(contentTypeId: ContentTypeId, seriesName: SeriesName): F[Seq[(Int, ResponseArticle)]]
}

object ArticleRepository {

import doobie.Read
import doobie.ConnectionIO
import net.yoshinorin.qualtet.domains.contents.{ContentId, Path}

given ArticleRepository: ArticleRepository[ConnectionIO] = {
new ArticleRepository[ConnectionIO] {
given responseArticleWithCountRead: Read[(Int, ResponseArticle)] =
Read[(Int, (String, String, String, String, Long, Long))].map { case (cnt, (id, path, title, content, publishedAt, updatedAt)) =>
(cnt, ResponseArticle(ContentId(id), Path(path), title, content, publishedAt, updatedAt))
}

override def getWithCount(contentTypeId: ContentTypeId, sqlParams: SqlParams): ConnectionIO[Seq[(Int, ResponseArticle)]] = {
ArticleQuery.getWithCount(contentTypeId, sqlParams).to[Seq]
}
override def findByTagNameWithCount(contentTypeId: ContentTypeId, tagName: TagName, sqlParams: SqlParams): ConnectionIO[Seq[(Int, ResponseArticle)]] = {
ArticleQuery.findByTagNameWithCount(contentTypeId, tagName, sqlParams).to[Seq]
}

override def findBySeriesNameWithCount(contentTypeId: ContentTypeId, seriesName: SeriesName): ConnectionIO[Seq[(Int, ResponseArticle)]] = {
ArticleQuery.findBySeriesNameWithCount(contentTypeId, seriesName).to[Seq]
}
}
}

}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,51 @@ trait AuthorRepository[F[_]] {
*/
def findByName(name: AuthorName): F[Option[ResponseAuthor]]
}

object AuthorRepository {

import doobie.{Read, Write}
import doobie.ConnectionIO

given AuthorRepository: AuthorRepository[ConnectionIO] = {
new AuthorRepository[ConnectionIO] {

given responseAuthorRead: Read[ResponseAuthor] =
Read[(String, String, String, Long)].map { case (id, name, displayName, createdAt) =>
ResponseAuthor(AuthorId(id), AuthorName(name), AuthorDisplayName(displayName), createdAt)
}

given responseAuthorWithOptionRead: Read[Option[ResponseAuthor]] =
Read[(String, String, String, Long)].map { case (id, name, displayName, createdAt) =>
Some(ResponseAuthor(AuthorId(id), AuthorName(name), AuthorDisplayName(displayName), createdAt))
}

given authorRead: Read[Author] =
Read[(String, String, String, String, Long)].map { case (id, name, displayName, password, createdAt) =>
Author(AuthorId(id), AuthorName(name), AuthorDisplayName(displayName), BCryptPassword(password), createdAt)
}

given authorWithOptionRead: Read[Option[Author]] =
Read[(String, String, String, String, Long)].map { case (id, name, displayName, password, createdAt) =>
Some(Author(AuthorId(id), AuthorName(name), AuthorDisplayName(displayName), BCryptPassword(password), createdAt))
}

given responseAuthorWrite: Write[Author] =
Write[(String, String, String, String, Long)]
.contramap(a => (a.id.value, a.name.value, a.displayName.value, a.password.value, a.createdAt))

override def getAll(): ConnectionIO[Seq[ResponseAuthor]] = AuthorQuery.getAll.to[Seq]

// TODO: Do not do `run` here
override def upsert(data: Author): ConnectionIO[Int] = AuthorQuery.upsert.run(data)

override def findById(id: AuthorId): ConnectionIO[Option[ResponseAuthor]] = AuthorQuery.findById(id).option

override def findByIdWithPassword(id: AuthorId): ConnectionIO[Option[Author]] = AuthorQuery.findByIdWithPassword(id).option

override def findByName(name: AuthorName): ConnectionIO[Option[ResponseAuthor]] = AuthorQuery.findByName(name).option

}
}

}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,50 @@ trait ContentSerializingRepository[F[_]] {
def fakeRequestInt: F[Int]
def fakeRequestUnit: F[Unit]
}

object ContentSerializingRepository {

import cats.implicits.catsSyntaxApplicativeId
import doobie.{Read, Write}
import doobie.ConnectionIO

given ContentSerializingRepository: ContentSerializingRepository[ConnectionIO] = {
new ContentSerializingRepository[ConnectionIO] {
given contentSerializingRead: Read[ContentSerializing] =
Read[(String, String)].map { case (seriesId, contentId) => ContentSerializing(SeriesId(seriesId), ContentId(contentId)) }

given contentSerializingWithOptionRead: Read[Option[ContentSerializing]] =
Read[(String, String)].map { case (seriesId, contentId) => Some(ContentSerializing(SeriesId(seriesId), ContentId(contentId))) }

given contentSerializingWrite: Write[ContentSerializing] =
Write[(String, String)].contramap(s => (s.seriesId.value, s.contentId.value))

override def upsert(data: ContentSerializing): ConnectionIO[Int] = {
ContentSerializingQuery.bulkUpsert.run(data)
}

override def bulkUpsert(data: List[ContentSerializing]): ConnectionIO[Int] = {
ContentSerializingQuery.bulkUpsert.updateMany(data)
}

override def findBySeriesId(id: SeriesId): ConnectionIO[Seq[ContentSerializing]] = {
ContentSerializingQuery.findBySeriesId(id).to[Seq]
}

override def deleteBySeriesId(id: SeriesId): ConnectionIO[Unit] = {
ContentSerializingQuery.deleteBySeriesId(id).option.map(_ => ())
}

override def deleteByContentId(id: ContentId): ConnectionIO[Unit] = {
ContentSerializingQuery.deleteByContentId(id).option.map(_ => ())
}

override def delete(seriesId: SeriesId, contentIds: Seq[ContentId]): ConnectionIO[Unit] = {
ContentSerializingQuery.delete(seriesId, contentIds).option.map(_ => ())
}
override def fakeRequestInt: ConnectionIO[Int] = 0.pure[ConnectionIO]
override def fakeRequestUnit: ConnectionIO[Unit] = ().pure[ConnectionIO]
}
}

}
Loading

0 comments on commit 8ef223d

Please sign in to comment.