Skip to content

Instantly share code, notes, and snippets.

View justinhj's full-sized avatar

Justin Heyes-Jones justinhj

View GitHub Profile
package org.justinhj
import zio.blocking.Blocking
import zio.clock.Clock
import zio.console._
import zio.random.Random
import zio.system.System
import zio.internal.PlatformLive
import zio.DefaultRuntime
import zio._
val finalState2 = finalEvents.map(eventToState).combineAll
println(show"Final state 2:\n$finalState2")
/* Output:
Final state 2:
Account holder: Ben Johnson
Balance: 80
*/
val (finalEntity, finalEvents) =
commands.foldLeft((sampleAccount, List.empty[BankAccountEvent])) {
case ((acc, events), cmd) =>
val newEvents = acc.processCommand(cmd)
val newAcc = newEvents.foldLeft(acc) {
case (acc, evt) =>
acc.processEvent(evt)
}
(newAcc, events ++ newEvents)
}
val t1 = Instant.now
val sampleAccount = AccountEntity(1, AccountState(0, None))
val commands = List(
DepositCmd(t1.plusSeconds(10), 100),
PurchaseCmd(t1.plusSeconds(20), 120),
AssignAccountHolderCmd(t1.plusSeconds(40), "Bob Johnson"),
DepositCmd(t1.plusSeconds(40), 100),
AssignAccountHolderCmd(t1.plusSeconds(50), "Ben Johnson"),
case class AccountEntity(id: Int, state: AccountState)
extends PersistentEntity[Int, AccountEntity] {
override type Command = BankAccountCommand
override type Event = BankAccountEvent
override type State = AccountState
// Applies the command to the current state, returning a list of events
def processCommand(command: Command) : List[Event] = {
command match {
def eventToState(event: BankAccountEvent): AccountState = {
event match {
case DepositEvt(time, amount) =>
AccountState(amount, None)
case PurchaseEvt(time, amount) =>
AccountState(-amount, None)
case AssignAccountHolderEvt(time, accountHolder) =>
AccountState(0, accountHolder.some)
}
}
// When the left and right hand side are set we take the right side
(LastOption("Nero".some) |+| LastOption("Titus".some)).show
// res1: String = "Titus"
// When the left is None and the right hand side is set we also take the right side
(LastOption(None: Option[String]) |+| LastOption("Titus".some)).show
// res2: String = "Titus"
// When the left side is set and the right side is None we keep the left side
(LastOption("Nero".some) |+| LastOption(None: Option[String])).show
AccountState(100, "Nero".some) |+| AccountState(120, "Titus".some)
// res1: AccountState = AccountState(220, Some("NeroTitus"))
case class AccountState(balance: Int, accountHolder: LastOption[String])
object AccountState {
implicit def accountStateShow[A] = new Show[AccountState] {
def show(a: AccountState): String = {
show"Balance: ${a.balance}\nAccount holder: ${a.accountHolder}"
}
}
implicit val accountMonoid = new Monoid[AccountState] {
sealed trait BankAccountCommand
case class DepositCmd(time: Instant, amount: Int) extends BankAccountCommand
case class PurchaseCmd(time: Instant, amount: Int) extends BankAccountCommand
case class AssignAccountHolderCmd(time: Instant, accountHolder: String) extends BankAccountCommand
sealed trait BankAccountEvent
case class DepositEvt(time: Instant, amount: Int) extends BankAccountEvent
case class PurchaseEvt(time: Instant, amount: Int) extends BankAccountEvent
case class AssignAccountHolderEvt(time: Instant, accountHolder: String) extends BankAccountEvent