remove reply type parameter from Command in Account example
* only makes it look more complex than it is * probably a remaining of the early reply experiments
This commit is contained in:
parent
c45e6ef39b
commit
888580e604
8 changed files with 91 additions and 79 deletions
|
|
@ -38,10 +38,10 @@ public interface AccountExampleWithEventHandlersInState {
|
|||
|
||||
// Command
|
||||
// #reply-command
|
||||
interface Command<Reply> extends CborSerializable {}
|
||||
interface Command extends CborSerializable {}
|
||||
// #reply-command
|
||||
|
||||
public static class CreateAccount implements Command<OperationResult> {
|
||||
public static class CreateAccount implements Command {
|
||||
public final ActorRef<OperationResult> replyTo;
|
||||
|
||||
@JsonCreator
|
||||
|
|
@ -50,7 +50,7 @@ public interface AccountExampleWithEventHandlersInState {
|
|||
}
|
||||
}
|
||||
|
||||
public static class Deposit implements Command<OperationResult> {
|
||||
public static class Deposit implements Command {
|
||||
public final BigDecimal amount;
|
||||
public final ActorRef<OperationResult> replyTo;
|
||||
|
||||
|
|
@ -60,7 +60,7 @@ public interface AccountExampleWithEventHandlersInState {
|
|||
}
|
||||
}
|
||||
|
||||
public static class Withdraw implements Command<OperationResult> {
|
||||
public static class Withdraw implements Command {
|
||||
public final BigDecimal amount;
|
||||
public final ActorRef<OperationResult> replyTo;
|
||||
|
||||
|
|
@ -70,7 +70,7 @@ public interface AccountExampleWithEventHandlersInState {
|
|||
}
|
||||
}
|
||||
|
||||
public static class GetBalance implements Command<CurrentBalance> {
|
||||
public static class GetBalance implements Command {
|
||||
public final ActorRef<CurrentBalance> replyTo;
|
||||
|
||||
@JsonCreator
|
||||
|
|
@ -79,7 +79,7 @@ public interface AccountExampleWithEventHandlersInState {
|
|||
}
|
||||
}
|
||||
|
||||
public static class CloseAccount implements Command<OperationResult> {
|
||||
public static class CloseAccount implements Command {
|
||||
public final ActorRef<OperationResult> replyTo;
|
||||
|
||||
@JsonCreator
|
||||
|
|
|
|||
|
|
@ -35,9 +35,9 @@ public interface AccountExampleWithMutableState {
|
|||
EntityTypeKey.create(Command.class, "Account");
|
||||
|
||||
// Command
|
||||
interface Command<Reply> extends CborSerializable {}
|
||||
interface Command extends CborSerializable {}
|
||||
|
||||
public static class CreateAccount implements Command<OperationResult> {
|
||||
public static class CreateAccount implements Command {
|
||||
public final ActorRef<OperationResult> replyTo;
|
||||
|
||||
@JsonCreator
|
||||
|
|
@ -46,7 +46,7 @@ public interface AccountExampleWithMutableState {
|
|||
}
|
||||
}
|
||||
|
||||
public static class Deposit implements Command<OperationResult> {
|
||||
public static class Deposit implements Command {
|
||||
public final BigDecimal amount;
|
||||
public final ActorRef<OperationResult> replyTo;
|
||||
|
||||
|
|
@ -56,7 +56,7 @@ public interface AccountExampleWithMutableState {
|
|||
}
|
||||
}
|
||||
|
||||
public static class Withdraw implements Command<OperationResult> {
|
||||
public static class Withdraw implements Command {
|
||||
public final BigDecimal amount;
|
||||
public final ActorRef<OperationResult> replyTo;
|
||||
|
||||
|
|
@ -66,7 +66,7 @@ public interface AccountExampleWithMutableState {
|
|||
}
|
||||
}
|
||||
|
||||
public static class GetBalance implements Command<CurrentBalance> {
|
||||
public static class GetBalance implements Command {
|
||||
public final ActorRef<CurrentBalance> replyTo;
|
||||
|
||||
@JsonCreator
|
||||
|
|
@ -75,7 +75,7 @@ public interface AccountExampleWithMutableState {
|
|||
}
|
||||
}
|
||||
|
||||
public static class CloseAccount implements Command<OperationResult> {
|
||||
public static class CloseAccount implements Command {
|
||||
public final ActorRef<OperationResult> replyTo;
|
||||
|
||||
@JsonCreator
|
||||
|
|
|
|||
|
|
@ -35,9 +35,9 @@ public interface AccountExampleWithNullState {
|
|||
EntityTypeKey.create(Command.class, "Account");
|
||||
|
||||
// Command
|
||||
interface Command<Reply> extends CborSerializable {}
|
||||
interface Command extends CborSerializable {}
|
||||
|
||||
public static class CreateAccount implements Command<OperationResult> {
|
||||
public static class CreateAccount implements Command {
|
||||
public final ActorRef<OperationResult> replyTo;
|
||||
|
||||
@JsonCreator
|
||||
|
|
@ -46,7 +46,7 @@ public interface AccountExampleWithNullState {
|
|||
}
|
||||
}
|
||||
|
||||
public static class Deposit implements Command<OperationResult> {
|
||||
public static class Deposit implements Command {
|
||||
public final BigDecimal amount;
|
||||
public final ActorRef<OperationResult> replyTo;
|
||||
|
||||
|
|
@ -56,7 +56,7 @@ public interface AccountExampleWithNullState {
|
|||
}
|
||||
}
|
||||
|
||||
public static class Withdraw implements Command<OperationResult> {
|
||||
public static class Withdraw implements Command {
|
||||
public final BigDecimal amount;
|
||||
public final ActorRef<OperationResult> replyTo;
|
||||
|
||||
|
|
@ -66,7 +66,7 @@ public interface AccountExampleWithNullState {
|
|||
}
|
||||
}
|
||||
|
||||
public static class GetBalance implements Command<CurrentBalance> {
|
||||
public static class GetBalance implements Command {
|
||||
public final ActorRef<CurrentBalance> replyTo;
|
||||
|
||||
@JsonCreator
|
||||
|
|
@ -75,7 +75,7 @@ public interface AccountExampleWithNullState {
|
|||
}
|
||||
}
|
||||
|
||||
public static class CloseAccount implements Command<OperationResult> {
|
||||
public static class CloseAccount implements Command {
|
||||
public final ActorRef<OperationResult> replyTo;
|
||||
|
||||
@JsonCreator
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ class AccountExampleDocSpec
|
|||
with LogCapturing {
|
||||
|
||||
private val eventSourcedTestKit =
|
||||
EventSourcedBehaviorTestKit[AccountEntity.Command[_], AccountEntity.Event, AccountEntity.Account](
|
||||
EventSourcedBehaviorTestKit[AccountEntity.Command, AccountEntity.Event, AccountEntity.Account](
|
||||
system,
|
||||
AccountEntity("1", PersistenceId("Account", "1")))
|
||||
|
||||
|
|
|
|||
|
|
@ -79,10 +79,8 @@ class AccountExampleSpec
|
|||
}
|
||||
|
||||
"reject Withdraw overdraft" in {
|
||||
// AccountCommand[_] is the command type, but it should also be possible to narrow it to
|
||||
// AccountCommand[OperationResult]
|
||||
val probe = createTestProbe[OperationResult]()
|
||||
val ref = ClusterSharding(system).entityRefFor[Command[OperationResult]](AccountEntity.TypeKey, "3")
|
||||
val ref = ClusterSharding(system).entityRefFor[Command](AccountEntity.TypeKey, "3")
|
||||
ref ! CreateAccount(probe.ref)
|
||||
probe.expectMessage(Confirmed)
|
||||
ref ! Deposit(100, probe.ref)
|
||||
|
|
@ -90,10 +88,12 @@ class AccountExampleSpec
|
|||
ref ! Withdraw(110, probe.ref)
|
||||
probe.expectMessageType[Rejected]
|
||||
|
||||
// Account.Command is the command type, but it should also be possible to narrow it
|
||||
// ... thus restricting the entity ref from being sent other commands, e.g.:
|
||||
// val ref2 = ClusterSharding(system).entityRefFor[Deposit](AccountEntity.TypeKey, "3")
|
||||
// val probe2 = createTestProbe[CurrentBalance]()
|
||||
// val msg = GetBalance(probe2.ref)
|
||||
// ref ! msg // type mismatch: GetBalance NOT =:= AccountCommand[OperationResult]
|
||||
// ref2 ! msg // type mismatch: GetBalance NOT =:= Deposit
|
||||
}
|
||||
|
||||
"handle GetBalance" in {
|
||||
|
|
|
|||
|
|
@ -24,14 +24,12 @@ object AccountExampleWithCommandHandlersInState {
|
|||
//#account-entity
|
||||
object AccountEntity {
|
||||
// Command
|
||||
sealed trait Command[Reply <: CommandReply] extends CborSerializable {
|
||||
def replyTo: ActorRef[Reply]
|
||||
}
|
||||
final case class CreateAccount(replyTo: ActorRef[OperationResult]) extends Command[OperationResult]
|
||||
final case class Deposit(amount: BigDecimal, replyTo: ActorRef[OperationResult]) extends Command[OperationResult]
|
||||
final case class Withdraw(amount: BigDecimal, replyTo: ActorRef[OperationResult]) extends Command[OperationResult]
|
||||
final case class GetBalance(replyTo: ActorRef[CurrentBalance]) extends Command[CurrentBalance]
|
||||
final case class CloseAccount(replyTo: ActorRef[OperationResult]) extends Command[OperationResult]
|
||||
sealed trait Command extends CborSerializable
|
||||
final case class CreateAccount(replyTo: ActorRef[OperationResult]) extends Command
|
||||
final case class Deposit(amount: BigDecimal, replyTo: ActorRef[OperationResult]) extends Command
|
||||
final case class Withdraw(amount: BigDecimal, replyTo: ActorRef[OperationResult]) extends Command
|
||||
final case class GetBalance(replyTo: ActorRef[CurrentBalance]) extends Command
|
||||
final case class CloseAccount(replyTo: ActorRef[OperationResult]) extends Command
|
||||
|
||||
// Reply
|
||||
sealed trait CommandReply extends CborSerializable
|
||||
|
|
@ -54,11 +52,11 @@ object AccountExampleWithCommandHandlersInState {
|
|||
|
||||
// State
|
||||
sealed trait Account extends CborSerializable {
|
||||
def applyCommand(cmd: Command[_]): ReplyEffect
|
||||
def applyCommand(cmd: Command): ReplyEffect
|
||||
def applyEvent(event: Event): Account
|
||||
}
|
||||
case object EmptyAccount extends Account {
|
||||
override def applyCommand(cmd: Command[_]): ReplyEffect =
|
||||
override def applyCommand(cmd: Command): ReplyEffect =
|
||||
cmd match {
|
||||
case CreateAccount(replyTo) =>
|
||||
Effect.persist(AccountCreated).thenReply(replyTo)(_ => Confirmed)
|
||||
|
|
@ -76,7 +74,7 @@ object AccountExampleWithCommandHandlersInState {
|
|||
case class OpenedAccount(balance: BigDecimal) extends Account {
|
||||
require(balance >= Zero, "Account balance can't be negative")
|
||||
|
||||
override def applyCommand(cmd: Command[_]): ReplyEffect =
|
||||
override def applyCommand(cmd: Command): ReplyEffect =
|
||||
cmd match {
|
||||
case Deposit(amount, replyTo) =>
|
||||
Effect.persist(Deposited(amount)).thenReply(replyTo)(_ => Confirmed)
|
||||
|
|
@ -115,28 +113,33 @@ object AccountExampleWithCommandHandlersInState {
|
|||
|
||||
}
|
||||
case object ClosedAccount extends Account {
|
||||
override def applyCommand(cmd: Command[_]): ReplyEffect =
|
||||
override def applyCommand(cmd: Command): ReplyEffect =
|
||||
cmd match {
|
||||
case c @ (_: Deposit | _: Withdraw) =>
|
||||
Effect.reply(c.replyTo)(Rejected("Account is closed"))
|
||||
case c: Deposit =>
|
||||
replyClosed(c.replyTo)
|
||||
case c: Withdraw =>
|
||||
replyClosed(c.replyTo)
|
||||
case GetBalance(replyTo) =>
|
||||
Effect.reply(replyTo)(CurrentBalance(Zero))
|
||||
case CloseAccount(replyTo) =>
|
||||
Effect.reply(replyTo)(Rejected("Account is already closed"))
|
||||
replyClosed(replyTo)
|
||||
case CreateAccount(replyTo) =>
|
||||
Effect.reply(replyTo)(Rejected("Account is already created"))
|
||||
replyClosed(replyTo)
|
||||
}
|
||||
|
||||
private def replyClosed(replyTo: ActorRef[AccountEntity.OperationResult]): ReplyEffect =
|
||||
Effect.reply(replyTo)(Rejected(s"Account is closed"))
|
||||
|
||||
override def applyEvent(event: Event): Account =
|
||||
throw new IllegalStateException(s"unexpected event [$event] in state [ClosedAccount]")
|
||||
}
|
||||
|
||||
// when used with sharding, this TypeKey can be used in `sharding.init` and `sharding.entityRefFor`:
|
||||
val TypeKey: EntityTypeKey[Command[_]] =
|
||||
EntityTypeKey[Command[_]]("Account")
|
||||
val TypeKey: EntityTypeKey[Command] =
|
||||
EntityTypeKey[Command]("Account")
|
||||
|
||||
def apply(persistenceId: PersistenceId): Behavior[Command[_]] = {
|
||||
EventSourcedBehavior.withEnforcedReplies[Command[_], Event, Account](
|
||||
def apply(persistenceId: PersistenceId): Behavior[Command] = {
|
||||
EventSourcedBehavior.withEnforcedReplies[Command, Event, Account](
|
||||
persistenceId,
|
||||
EmptyAccount,
|
||||
(state, cmd) => state.applyCommand(cmd),
|
||||
|
|
|
|||
|
|
@ -27,17 +27,15 @@ object AccountExampleWithEventHandlersInState {
|
|||
object AccountEntity {
|
||||
// Command
|
||||
//#reply-command
|
||||
sealed trait Command[Reply <: CommandReply] extends CborSerializable {
|
||||
def replyTo: ActorRef[Reply]
|
||||
}
|
||||
sealed trait Command extends CborSerializable
|
||||
//#reply-command
|
||||
final case class CreateAccount(replyTo: ActorRef[OperationResult]) extends Command[OperationResult]
|
||||
final case class Deposit(amount: BigDecimal, replyTo: ActorRef[OperationResult]) extends Command[OperationResult]
|
||||
final case class CreateAccount(replyTo: ActorRef[OperationResult]) extends Command
|
||||
final case class Deposit(amount: BigDecimal, replyTo: ActorRef[OperationResult]) extends Command
|
||||
//#reply-command
|
||||
final case class Withdraw(amount: BigDecimal, replyTo: ActorRef[OperationResult]) extends Command[OperationResult]
|
||||
final case class Withdraw(amount: BigDecimal, replyTo: ActorRef[OperationResult]) extends Command
|
||||
//#reply-command
|
||||
final case class GetBalance(replyTo: ActorRef[CurrentBalance]) extends Command[CurrentBalance]
|
||||
final case class CloseAccount(replyTo: ActorRef[OperationResult]) extends Command[OperationResult]
|
||||
final case class GetBalance(replyTo: ActorRef[CurrentBalance]) extends Command
|
||||
final case class CloseAccount(replyTo: ActorRef[OperationResult]) extends Command
|
||||
|
||||
// Reply
|
||||
//#reply-command
|
||||
|
|
@ -89,20 +87,20 @@ object AccountExampleWithEventHandlersInState {
|
|||
}
|
||||
|
||||
// when used with sharding, this TypeKey can be used in `sharding.init` and `sharding.entityRefFor`:
|
||||
val TypeKey: EntityTypeKey[Command[_]] =
|
||||
EntityTypeKey[Command[_]]("Account")
|
||||
val TypeKey: EntityTypeKey[Command] =
|
||||
EntityTypeKey[Command]("Account")
|
||||
|
||||
// Note that after defining command, event and state classes you would probably start here when writing this.
|
||||
// When filling in the parameters of EventSourcedBehavior.apply you can use IntelliJ alt+Enter > createValue
|
||||
// to generate the stub with types for the command and event handlers.
|
||||
|
||||
//#withEnforcedReplies
|
||||
def apply(accountNumber: String, persistenceId: PersistenceId): Behavior[Command[_]] = {
|
||||
def apply(accountNumber: String, persistenceId: PersistenceId): Behavior[Command] = {
|
||||
EventSourcedBehavior.withEnforcedReplies(persistenceId, EmptyAccount, commandHandler(accountNumber), eventHandler)
|
||||
}
|
||||
//#withEnforcedReplies
|
||||
|
||||
private def commandHandler(accountNumber: String): (Account, Command[_]) => ReplyEffect[Event, Account] = {
|
||||
private def commandHandler(accountNumber: String): (Account, Command) => ReplyEffect[Event, Account] = {
|
||||
(state, cmd) =>
|
||||
state match {
|
||||
case EmptyAccount =>
|
||||
|
|
@ -122,18 +120,26 @@ object AccountExampleWithEventHandlersInState {
|
|||
|
||||
case ClosedAccount =>
|
||||
cmd match {
|
||||
case c @ (_: Deposit | _: Withdraw) =>
|
||||
Effect.reply(c.replyTo)(Rejected(s"Account $accountNumber is closed"))
|
||||
case c: Deposit =>
|
||||
replyClosed(accountNumber, c.replyTo)
|
||||
case c: Withdraw =>
|
||||
replyClosed(accountNumber, c.replyTo)
|
||||
case GetBalance(replyTo) =>
|
||||
Effect.reply(replyTo)(CurrentBalance(Zero))
|
||||
case CloseAccount(replyTo) =>
|
||||
Effect.reply(replyTo)(Rejected(s"Account $accountNumber is already closed"))
|
||||
replyClosed(accountNumber, replyTo)
|
||||
case CreateAccount(replyTo) =>
|
||||
Effect.reply(replyTo)(Rejected(s"Account $accountNumber is already closed"))
|
||||
replyClosed(accountNumber, replyTo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def replyClosed(
|
||||
accountNumber: String,
|
||||
replyTo: ActorRef[AccountEntity.OperationResult]): ReplyEffect[Event, Account] = {
|
||||
Effect.reply(replyTo)(Rejected(s"Account $accountNumber is closed"))
|
||||
}
|
||||
|
||||
private val eventHandler: (Account, Event) => Account = { (state, event) =>
|
||||
state.applyEvent(event)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,14 +24,12 @@ object AccountExampleWithOptionState {
|
|||
//#account-entity
|
||||
object AccountEntity {
|
||||
// Command
|
||||
sealed trait Command[Reply <: CommandReply] extends CborSerializable {
|
||||
def replyTo: ActorRef[Reply]
|
||||
}
|
||||
final case class CreateAccount(replyTo: ActorRef[OperationResult]) extends Command[OperationResult]
|
||||
final case class Deposit(amount: BigDecimal, replyTo: ActorRef[OperationResult]) extends Command[OperationResult]
|
||||
final case class Withdraw(amount: BigDecimal, replyTo: ActorRef[OperationResult]) extends Command[OperationResult]
|
||||
final case class GetBalance(replyTo: ActorRef[CurrentBalance]) extends Command[CurrentBalance]
|
||||
final case class CloseAccount(replyTo: ActorRef[OperationResult]) extends Command[OperationResult]
|
||||
sealed trait Command extends CborSerializable
|
||||
final case class CreateAccount(replyTo: ActorRef[OperationResult]) extends Command
|
||||
final case class Deposit(amount: BigDecimal, replyTo: ActorRef[OperationResult]) extends Command
|
||||
final case class Withdraw(amount: BigDecimal, replyTo: ActorRef[OperationResult]) extends Command
|
||||
final case class GetBalance(replyTo: ActorRef[CurrentBalance]) extends Command
|
||||
final case class CloseAccount(replyTo: ActorRef[OperationResult]) extends Command
|
||||
|
||||
// Reply
|
||||
sealed trait CommandReply extends CborSerializable
|
||||
|
|
@ -54,13 +52,13 @@ object AccountExampleWithOptionState {
|
|||
|
||||
// State
|
||||
sealed trait Account extends CborSerializable {
|
||||
def applyCommand(cmd: Command[_]): ReplyEffect
|
||||
def applyCommand(cmd: Command): ReplyEffect
|
||||
def applyEvent(event: Event): Account
|
||||
}
|
||||
case class OpenedAccount(balance: BigDecimal) extends Account {
|
||||
require(balance >= Zero, "Account balance can't be negative")
|
||||
|
||||
override def applyCommand(cmd: Command[_]): ReplyEffect =
|
||||
override def applyCommand(cmd: Command): ReplyEffect =
|
||||
cmd match {
|
||||
case Deposit(amount, replyTo) =>
|
||||
Effect.persist(Deposited(amount)).thenReply(replyTo)(_ => Confirmed)
|
||||
|
|
@ -99,28 +97,33 @@ object AccountExampleWithOptionState {
|
|||
|
||||
}
|
||||
case object ClosedAccount extends Account {
|
||||
override def applyCommand(cmd: Command[_]): ReplyEffect =
|
||||
override def applyCommand(cmd: Command): ReplyEffect =
|
||||
cmd match {
|
||||
case c @ (_: Deposit | _: Withdraw) =>
|
||||
Effect.reply(c.replyTo)(Rejected("Account is closed"))
|
||||
case c: Deposit =>
|
||||
replyClosed(c.replyTo)
|
||||
case c: Withdraw =>
|
||||
replyClosed(c.replyTo)
|
||||
case GetBalance(replyTo) =>
|
||||
Effect.reply(replyTo)(CurrentBalance(Zero))
|
||||
case CloseAccount(replyTo) =>
|
||||
Effect.reply(replyTo)(Rejected("Account is already closed"))
|
||||
replyClosed(replyTo)
|
||||
case CreateAccount(replyTo) =>
|
||||
Effect.reply(replyTo)(Rejected("Account is already created"))
|
||||
replyClosed(replyTo)
|
||||
}
|
||||
|
||||
private def replyClosed(replyTo: ActorRef[AccountEntity.OperationResult]): ReplyEffect =
|
||||
Effect.reply(replyTo)(Rejected(s"Account is closed"))
|
||||
|
||||
override def applyEvent(event: Event): Account =
|
||||
throw new IllegalStateException(s"unexpected event [$event] in state [ClosedAccount]")
|
||||
}
|
||||
|
||||
// when used with sharding, this TypeKey can be used in `sharding.init` and `sharding.entityRefFor`:
|
||||
val TypeKey: EntityTypeKey[Command[_]] =
|
||||
EntityTypeKey[Command[_]]("Account")
|
||||
val TypeKey: EntityTypeKey[Command] =
|
||||
EntityTypeKey[Command]("Account")
|
||||
|
||||
def apply(persistenceId: PersistenceId): Behavior[Command[_]] = {
|
||||
EventSourcedBehavior.withEnforcedReplies[Command[_], Event, Option[Account]](
|
||||
def apply(persistenceId: PersistenceId): Behavior[Command] = {
|
||||
EventSourcedBehavior.withEnforcedReplies[Command, Event, Option[Account]](
|
||||
persistenceId,
|
||||
None,
|
||||
(state, cmd) =>
|
||||
|
|
@ -135,7 +138,7 @@ object AccountExampleWithOptionState {
|
|||
})
|
||||
}
|
||||
|
||||
def onFirstCommand(cmd: Command[_]): ReplyEffect = {
|
||||
def onFirstCommand(cmd: Command): ReplyEffect = {
|
||||
cmd match {
|
||||
case CreateAccount(replyTo) =>
|
||||
Effect.persist(AccountCreated).thenReply(replyTo)(_ => Confirmed)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue