Two tables: session
and clickcollect
ZIO
def insertSession(userId: ClickCollectUserId, state: State): UIO[(Session, ClickCollectRecord[State])] =
for
now <- Clock.instant
(initialSession, record) <- insertClickCollectQuery(userId, state, now).transaction
_ <- ZIO.foreachDiscard(initialSession.existingStartedAt)(time =>
ZIO.logWarning(s"Still existing session when initiating new session. Existing session started at ${time.toString}")
)
yield (initialSession.session, record)
ConnectionIO
private def insertClickCollectQuery(userId: ClickCollectUserId, state: State, now: Instant): ConnectionIO[(InitResult, ClickCollectRecord[State])] = for
initialSession <- SessionRepository.insertQuery(userId, now)
record <- insertQuery(initialSession.session.id, state, now).unique
yield (initialSession, record)
trait SessionRepository{
def insertQuery(userId: UserId, now: Instant): ConnectionIO[InitResult] = for
maybeActive <- SessionRepository.findActiveQuery(userId).option
_ <- maybeActive.traverse(active => SessionRepository.setEndedAtQuery(active.id, now).run)
session = Session.initial(userId, now)
_ <- SessionRepository.insertQuery(session).run
yield InitResult(session, maybeActive.map(_.createdAt))
}
ZIO[R,E,A]
but the Doobie sql parts are ConnectionIO
Two worlds
F[_]
@Transactional
public void createCourse(Course course) {
courseDao.create(course);
throw new DataIntegrityViolationException("Throwing exception for demoing Rollback!!!");
}
How do we do that?
ZIO
R
environmentConnectionIO
work?Transactor
contains
Connection
Strategy
which knows what to do before, after, onErrorsql program ⇒ interpreter ⇒ connection ⇒ result
ConnectionIO[A]
is a
Free[ConnectionOp, A]
a program of ConnectionOp
that can be interpreted and will return A
a ConnectionOp
is something you can do with a jdbc transaction. Do an insert, set a savepoint, rollback etc
ConnectionOp ~> Kleisli[Task, Connection, *]
~>
is a FunctionK
A[_]
to B[_]
ZIO.fromTry
transforms a Try[A]
to a Task[A]
Kleisli[Task, A, B]
is a function A => Task[B]
this is something we can do as a zio... 💡
A simple sql program. ZIO code and transactions live in separate worlds.
(for comparison)
Seedemo.DoobieZio0.scala
Transactional scoping of our zio code that we see in the types
Seedemo.DoobieZio1.scala
Transactional scoping of our zio code that we don't see in the types
Seedemo.DoobieZio2.scala
Transactional scoping of our zio code with scala 3 context functions
Seedemo.DoobieZio3.scala
But then you still have two separate worlds: zio and ConnectionIO