Whilst working on a fairly large Akka application, I have come across a very simple structure when working with normal methods and non Akka classes but which are actually quite difficult to nail when working with Akka which is why I have come here to ask what you recommend to be the best way to solve this issue.
So the issue is this, I have one parent actor, let's call him "Connector", Connector has behavior describing what it should do when it receives a ConnectCommand instance. First it submit a form using an HttpClient, then it goes to a couple of URLs to check for some session parameters and eventually sends the Sender (Referred to as the "Consumer") a connection message containing everything it needs to use the API.
Now, I'm a big fan of tell, not so much of pull / ask so implementing this is in my opinion a hard task to do. Let's go over it. All the responses returned by the HttpClientActor are a Response instance, so what first came to mind is having multiple behaviors defined in my actor and incrementally, after a certain step of the connection process has been completed, change behavior to the next step.
private final PartialFunction<Object, BoxedUnit> inital = ReceiveBuilder
.match(ConnectCommand.class, c -> this.startConnection())
.matchAny(this::unhandled)
.build();
private final PartialFunction<Object, BoxedUnit> stage1 = ReceiveBuilder
.match(Response.class, this::stage1)
.matchAny(this::unhandled)
.build();
private final PartialFunction<Object, BoxedUnit> stage2 = ReceiveBuilder
.match(Response.class, this::stage2)
.matchAny(this::unhandled)
.build();
private final PartialFunction<Object, BoxedUnit> stage3 = ReceiveBuilder
.match(Response.class, this::stage3)
.matchAny(this::unhandled)
.build();
private final PartialFunction<Object, BoxedUnit> stage4 = ReceiveBuilder
.match(Response.class, this::stage4)
.matchAny(this::unhandled)
.build();
private final PartialFunction<Object, BoxedUnit> stage5 = ReceiveBuilder
.match(Response.class, this::stage5)
.matchAny(this::unhandled)
.build();
private final PartialFunction<Object, BoxedUnit> stage6 = ReceiveBuilder
.match(Response.class, this::stage6)
.matchAny(this::unhandled)
.build();
private final PartialFunction<Object, BoxedUnit> stage7 = ReceiveBuilder
.match(Response.class, this::stage7)
.matchAny(this::unhandled)
.build();
This has the advantage that it is using tell, not ask but has the major drawback that code becomes very unreadable.
Now I'm at the point I feel like this Actor needs some change in a good way but there are two alternatives in my opinion.
The first one involves splitting up every HttpRequest and Response into a separate Actor and aggregating the results in the Connector actor. This has the advantage of being very readable, using tell and shouldn't hurt performance because Akka is built to handle lots of actors. The only drawback to this is that I need to create a lot of container classes for these parts of state that need to be delivered from Stage5Actor to the Connector actor. This makes for a large memory overhead (correct me if I'm wrong).
The second approach is using the Ask pattern to wire the steps together. This would result in a single Connector actor and since Spray does so too for it's Http Client I think it may be a valid solution. Only drawback to this is, because everything rests on top of an external Http API, timeouts may become an issue. If this approach is recommend by the Akka team, how does one handle with all the timeouts which are completely unpredictable.
Please note that this implementation needs to be able to use supervision strategies since our whole current approach is based on top of this.
If you feel there is a far better solution than the ones I mentioned, please do tell! I'm really enjoying Akka a.t.m. and every piece of advice I get it, is a gain in experience and knowledge, not only for me, but for the whole community :D. Plus I think this is an issue more people run into every once in a while.
Thanks in advance and a big thanks to the Akka team for producing such an awesome lib!
PS. This question was first asked on the Akka github itself but I decided to post it here because this is just as much an Actor model related question as an Akka related question.
Link to the issue on github: https://github.com/akka/akka/issues/16080