Skip to content

Instantly share code, notes, and snippets.

@xuwei-k
Last active October 14, 2015 11:59
Show Gist options
  • Save xuwei-k/469a2213c7773274272f to your computer and use it in GitHub Desktop.
Save xuwei-k/469a2213c7773274272f to your computer and use it in GitHub Desktop.
scalaVersion := "2.11.6"
libraryDependencies += "org.scalaz" %% "scalaz-core" % "7.1.1"
import scalaz._
import Free.FreeC
class DDD[Entity, ID] {
// repositoryへの操作を代数的データ型で定義し、それをFreeとCoyonedaの力によりモナドにして使う
sealed abstract class RepositoryDSL[A]
// 見つからないかもしれないので結果はOption型
final case class ResolveBy(id: ID) extends RepositoryDSL[Option[Entity]]
// storeするentityそのまま返す。必要ないなら、戻り値型Unitでもいいかも?
// それとも、IDは引数渡すのではなく、Repository側で発行すべき?
final case class Store(id: ID, entity: Entity) extends RepositoryDSL[Entity]
def resolveBy(id: ID): FreeC[RepositoryDSL, Option[Entity]] =
Free.liftFC(ResolveBy(id))
def store(id: ID, entity: Entity): FreeC[RepositoryDSL, Entity] =
Free.liftFC(Store(id, entity))
}
import scalaz._
import scalaz.Id.Id
object Sample {
type UserId = Int
final case class User(id: UserId, name: String)
// 型パラメータを当てはめるためだけにnewしてるけど、ここはもっと色々やり方ありそう
val ddd = new DDD[User, UserId]
// ダミーのデータベース
val database = collection.mutable.Map.empty[UserId, User]
// これは単純にscalazのId型(identityモナド)で、エラーが発生しない、エラー処理を行わないことにしてあるが、
// RepositoryDSL ~> Either 型へのインタプリタを作ることにより、エラーを処理することも可能
val interpreter = new (ddd.RepositoryDSL ~> Id) {
def apply[A](fa: ddd.RepositoryDSL[A]) = fa match {
case ddd.ResolveBy(id) =>
database.get(id)
case ddd.Store(id, entity) =>
database.put(id, entity)
entity
}
}
val user1 = User(1, "aaa")
val user2 = User(2, "bbb")
def main(args: Array[String]): Unit = {
// for式でプログラムを組み立てる
// 組み立てるだけで、実際にインタプリタを渡さないと、まだ実行はされない
val program = for {
_ <- ddd.store(user1.id, user1)
_ <- ddd.store(user2.id, user2)
user <- ddd.resolveBy(user1.id)
} yield user
val result = Free.runFC(program)(interpreter)
println(result)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment