question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Review PersistentEntity API for reply and afterPersist

See original GitHub issue

This issue is a starting point for a broader discussion we would like to initiate about the current state of PersistentEntity API.

This was initially triggered by PR #901. The main problem reported there is that it’s very cumbersome to write command handlers that reply with the entity state while emitting many or none events.

For illustration: (examples in Java syntax)

final List<Event> createdEvents = generateEvents();
if (createdEvents.size() == 0) {
  ctx.reply(state().getModel());
  return ctx.done();
} else {
   return ctx.thenPersistAll(
                   createdEvents, 
                    () -> {  ctx.reply(state().getModel()); }
              );
}

In its current form, the API offers no easy way to deal with the fact that events may not be emitted.

The ctx.thenPersistAll method receive an empty lists of events, but the afterPersist method is not called if there are zero events persisted. Which make sense, because it’s called afterPersist.

The mechanism is based on aContext that is passed to the user. The Context brings to user space (without exposing it) the actor.sender(). In the case of reply, behind the scene the reply calls sender() ! someMessage.

Note that we are basically piggybacking the afterPersist method to call indirectly the actor.sender() method.

Fluent API Proposal

We could achieve exactly the same without relying on a context and without mixing side-effect function (afterPersist) with reply functions.

For instance…

PersistAll(generateEvents())
  .replyWith( state -> state.getVersion(); );

The above example replies by returning the version of model. The state is the model after applying the generated events. If generatedEvents return a empty list, state is not updated.

Note that this API removes completely the need for a Context. We just register a function State -> A that is called to transform the current state and generate the data that will be sent back to the sender.

Another variation could be:

PersistAll(generateEvents())
  .replyWith( (state, events) -> state.getVersion(); );

In that case, the user receives the current updated state and the generated events.

A similar fluent API can be implement for afterPersist.

PersistAll(generateEvents())
  .afterPersist( events -> // do some side-effect here ) // <- symmetric api, see below
  .replyWith( state -> state.getVersion(); );

afterPersist callback is only called if events.nonEmpty and it returns void or Unit (scala).

PersistOne / PersistAll asymmetry

The afterPersist callbacks for PersistOne and PersistAll are not symmetric.

PersistOne has Event -> void and PersistAll has () -> void.

The reason for that comes from akka-persistence itself where the handler for persistAll has the same shape (Event -> void) as the handler for persist. This is probably for historical reasons, but as a consequence the Lagom API exposed it as Event -> void and () -> void.

Some users have complained on Gitter about it. There is a easy workaround which is to have the generated events in scope when defining the command handler. However, we can easily improve the user experience by offering a symmetric API.

Issue Analytics

  • State:closed
  • Created 6 years ago
  • Reactions:4
  • Comments:16 (15 by maintainers)

github_iconTop GitHub Comments

3reactions
guizmaiicommented, Aug 9, 2017

As a first observation,

PersistAll(generateEvents()).replyWith( (state, events) -> state.getVersion(); );

seems better than

PersistAll(generateEvents()).replyWith( state -> state.getVersion(); );
1reaction
mihborcommented, Sep 26, 2017

I really like the explicit handling of entity creation. How would deletion be handled? By an event-handler that sets the state to None or an explicit API as well?

Read more comments on GitHub >

github_iconTop Results From Across the Web

Method: reviews.reply | Google Play Developer API
ReviewReplyResult. The result of replying/updating a reply to review. The reply text that was applied. The time at which the reply took effect....
Read more >
Java Persistence with Hibernate
Hibernate and specifications such as the Java Persistence API (JPA). ... Let's review some of the SQL terms used in this book. You...
Read more >
Java Persistence
Specifically using the Java Persistence API (JPA) to store Java objects to relational databases, but I would like it to have a somewhat ......
Read more >
Lift in Action - Perrett - Manning (2011).pdf
of the ways in which Scala provides a concise API for Lift, and these savings are cou- ... review each element within the...
Read more >
Google Play Developer API | Reply to Reviews
https://www.googleapis.com/androidpublisher/v2/applications/{your_package_name}/reviews/ {review_id}:reply?access_token={your_access_token}.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found