I am going to describe a concrete scenario I have: "*an item can be removed 
from the menu*"

class MenuActor extends PersistentActor {


var state: Option[Menu] = None


override def receiveCommand: Receive = {

 

case remove: RemoveItem =>

  persist(MenuItemRemoved(remove.itemId)) {

    event =>

      state = state.map(_.remove(event.itemId))

      sender ! state.get

  }

// ... other handlers 

} 

} 

The Menu aggregate root raises a *BusinessException("$itemId does not 
exist") *if obviously the item identified by the given ID does not exist in 
the Menu.

In the concrete case of an HTTP client this error would be map to a 400 
status code containing the descriptive message as any other "breaking 
aggregate invariant" scenario.

What I am doing right now is this:

class MenuActor extends PersistentActor {

var state: Option[Menu] = None


override def receiveCommand: Receive = {

case remove: RemoveItem =>

try {

  val newState = state.map(_.remove(remove.itemId)) *// -> 1*

  persist(MenuItemRemoved(remove.categoryId)) {

    event =>

      state = newState *// -> 2*

      sender ! state.get

  }

} catch {

  case businessException: BusinessException => sender() ! 
Failure(businessException) *// -> 3*

} 

// ... other handlers  

}

}


1 -> I mutate the actor state before persisting the event since that is the 
aggregate mutating behaviour that raise an exception when an invariant is 
violated. 
All the samples I saw mutates the internal actor state in the event handler 
closure after persisting the event but if the aggregate consistency is 
violated and it throws a business exception then we would have an 
inconsistent state of the journal containing an event that should not be 
there.

2 -> In order to prevent what I mentioned in the previous point I just 
assign the actor state to be the new state (aggregate mutation before 
persist)

3 -> If the aggregate raises an exception we don´t save the event in the 
journal, instead we send back a Failure message that maps to a 
Future.failed in the client who asked this actor and so we can give 
feedback to any client source about this business exception.


A )Is this OK? If not... how is handled?

I´ve seen in the docs that after sending back the *Failure* message, the 
exception is re-thrown so supervision takes place:

} catch {

case businessException: BusinessException => {  

sender() ! Failure(businessException) 

throw businessException // supervision take places 

} 

} 

Why to do that? The default supervision strategy will Restart the actor, 
but I don´t need the actor to be restarted, resumed would be OK, however... 
I just leave the actor as it is since the code above prevented the state to 
be mutated and the client who asked this actor is notified about the 
business exception.

B) Should I throw the businessException after sending back the Failure to 
the sender? Why? Why to restart the actor?

C) Last question, I´ve seen examples of supervision expecting 
NullPointerException, arithmeticException and so on... those sound bugs to 
me, unexpected exceptions other than network failures and so shouldn't´t be 
supervised but fixed. I understand the situation where a Backoff supervisor 
sounds reasonable to deal with network failures but I don´t see why to 
supervise exceptions that are caused because of bugs like the ones 
mentioned before (matching doc examples)


Thanks,
Sebastian




-- 
>>>>>>>>>>      Read the docs: http://akka.io/docs/
>>>>>>>>>>      Check the FAQ: 
>>>>>>>>>> http://doc.akka.io/docs/akka/current/additional/faq.html
>>>>>>>>>>      Search the archives: https://groups.google.com/group/akka-user
--- 
You received this message because you are subscribed to the Google Groups "Akka 
User List" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
Visit this group at https://groups.google.com/group/akka-user.
For more options, visit https://groups.google.com/d/optout.

Reply via email to