Last active
May 22, 2018 20:43
-
-
Save octonato/5af2ae584f154759d2793730082306ef to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package akka.persistence.typed.scaladsl | |
import akka.actor.typed.Behavior | |
import akka.persistence.typed.scaladsl.PersistentBehaviors.{CommandHandler, _} | |
class AccountSpec { | |
sealed trait AccountCommand | |
case object CreateAccount extends AccountCommand | |
case class Deposit(amount: Double) extends AccountCommand | |
case class Withdraw(amount: Double) extends AccountCommand | |
case object CloseAccount extends AccountCommand | |
sealed trait AccountEvent | |
case object AccountCreated extends AccountEvent | |
case class Deposited(amount: Double) extends AccountEvent | |
case class Withdrawn(amount: Double) extends AccountEvent | |
case object AccountClosed extends AccountEvent | |
sealed trait Account | |
case class OpenedAccount(balance: Double) extends Account | |
case object ClosedAccount extends Account | |
private def initial: CommandHandler[AccountCommand, AccountEvent, Option[Account]] = | |
(_, _, cmd) => | |
cmd match { | |
case CreateAccount ⇒ Effect.persist(AccountCreated) | |
} | |
private def openedAccountHandlers(acc: OpenedAccount): CommandHandler[AccountCommand, AccountEvent, Option[Account]] = | |
(ctx, _, cmd) => cmd match { | |
case Deposit(amount) ⇒ Effect.persist(Deposited(amount)) | |
case Withdraw(amount) if (acc.balance - amount) < 0.0 => | |
throw new RuntimeException("Insufficient balance") | |
case Withdraw(amount) => | |
Effect | |
.persist(Withdrawn(amount)) | |
.andThen { st => // can't even use a PF here because this method is overloaded | |
st match { // need to pattern match because state is Account, Effect is invariant on State | |
case Some(OpenedAccount(balance)) => | |
// do some side-effect using balance | |
println(balance) | |
} | |
} | |
case CloseAccount if acc.balance == 0.0 => | |
Effect.persist(AccountClosed) | |
case CloseAccount => | |
throw new RuntimeException("Account balance is not zero") | |
} | |
private def commandHandlers: CommandHandler[AccountCommand, AccountEvent, Option[Account]] = | |
CommandHandler.byState { | |
case None => CommandHandler.command { | |
case CreateAccount => Effect.persist(AccountCreated) | |
} | |
case Some(acc@OpenedAccount(_)) => openedAccountHandlers(acc) | |
case Some(ClosedAccount) => throw new RuntimeException("account already closed") | |
} | |
private def eventHandler(stateOpt: Option[Account], event: AccountEvent):Option[Account] = | |
(stateOpt, event) match { | |
case (None, AccountCreated) => Some(OpenedAccount(0.0)) | |
case (Some(acc@OpenedAccount(_)), Deposited(amount)) => | |
Some(acc.copy(balance = acc.balance + amount)) | |
case (Some(acc@OpenedAccount(_)), Withdrawn(amount)) => | |
Some(acc.copy(balance = acc.balance - amount)) | |
case (Some(acc@OpenedAccount(_)), AccountClosed) => | |
Some(ClosedAccount) | |
// this or compiler warning. We should not need to do it | |
case _ => throw new RuntimeException("this should never happen") | |
} | |
def behavior(accountNumber: String): Behavior[AccountCommand] = | |
PersistentBehaviors.receive[AccountCommand, AccountEvent, Option[Account]]( | |
persistenceId = accountNumber, | |
initialState = None, | |
commandHandler = commandHandlers, | |
eventHandler = eventHandler | |
) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package akka.persistence.typed.scaladsl | |
import akka.actor.typed.Behavior | |
import akka.persistence.typed.scaladsl.PersistentBehaviors.{CommandHandler, _} | |
class AccountSpec { | |
sealed trait AccountCommand | |
case object CreateAccount extends AccountCommand | |
case class Deposit(amount: Double) extends AccountCommand | |
case class Withdraw(amount: Double) extends AccountCommand | |
case object CloseAccount extends AccountCommand | |
sealed trait AccountEvent | |
case object AccountCreated extends AccountEvent | |
case class Deposited(amount: Double) extends AccountEvent | |
case class Withdrawn(amount: Double) extends AccountEvent | |
case object AccountClosed extends AccountEvent | |
sealed trait Account | |
case class OpenedAccount(balance: Double) extends Account | |
case object ClosedAccount extends Account | |
private def openedAccountHandler(acc: OpenedAccount): CommandHandler[AccountCommand, AccountEvent, Account] = | |
(ctx, _, cmd) => cmd match { | |
case Deposit(amount) ⇒ Effect.persist(Deposited(amount)) | |
case Withdraw(amount) if (acc.balance - amount) < 0.0 => | |
throw new RuntimeException("Insufficient balance") | |
case Withdraw(amount) => | |
Effect | |
.persist(Withdrawn(amount)) | |
.andThen { st => // can't even use a PF here because this method is overloaded | |
st match { // need to pattern match because state is Account, Effect is invariant on State | |
case OpenedAccount(balance) => | |
// do some side-effect using balance | |
println(balance) | |
} | |
} | |
case CloseAccount if acc.balance == 0.0 => | |
Effect.persist(AccountClosed) | |
case CloseAccount => | |
throw new RuntimeException("Account balance is not zero") | |
} | |
def openedAccountEvtHandler(acc: OpenedAccount): EventHandler[AccountEvent, Account] = { | |
case Deposited(amount) => | |
acc.copy(balance = acc.balance + amount) | |
case Withdrawn(amount) => | |
acc.copy(balance = acc.balance - amount) | |
case AccountClosed => ClosedAccount | |
// this or compiler warning. We should not need to do it | |
case _ => throw new RuntimeException("this should never happen") | |
} | |
def behavior(accountNumber: String): Behavior[AccountCommand] = | |
PersistentBehaviors[AccountCommand, AccountEvent, Account] | |
.identifiedBy(accountNumber) | |
.onCreation ( | |
commandHandler = CommandHandler.command { | |
case CreateAccount => Effect.persist(AccountCreated) | |
}, | |
eventHandler = { | |
case AccountCreated => OpenedAccount(0.0) | |
} | |
) | |
.onUpdate ( | |
commandHandler = { | |
case acc: OpenedAccount => openedAccountHandler(acc) | |
case ClosedAccount => throw new RuntimeException("account already closed") | |
}, | |
eventHandler = { | |
case acc: OpenedAccount => openedAccountEvtHandler(acc) | |
} | |
) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment