Disclaimer: The following blog post is VERY techie. If you’re hoping to learn all about Akka.NET and how it works, read on! You WON’T be disappointed. But since our normal readership is not quite this technical, we wanted to give you a heads up. If you start reading and have NO IDEA what’s going on, well, just know you’re not the only one. Ahh, the joys of being a non-programmer working at a custom software company…
For the past few months I have been learning about Akka.NET and actor systems. When I first came across Akka.NET, I really didn’t understand what it did or why you would use it. My googling about actors and actor systems didn’t expand my knowledge either.
That’s when I decided to work through the Akka.NET Bootcamp by Petabridge. Working through the lessons helped me see how powerful Akka.NET is and the potential it has. One of my first thoughts after completing the bootcamp was that Akka would be a perfect fit for creating a system that utilizes Event Sourcing.
As it turns out, I was not the first person to have this idea. The Akka.Persistence plugin was created exactly for this purpose. After reading more about it, I dove in, trying to learn as much as I could about Event Sourcing and Akka.Net by creating my own application. I am happy with what I’ve made so far but it wasn’t without challenges.
The system that I am trying to model is an online shopping cart. In this initial phase of development, I was able to complete four different use cases for the system.
First, the store owner can add new products to sell, setting the price and amount in inventory. They can also replenish the inventory of an existing product. A consumer could then browse the products in stock and add them to their individual cart. A total price of their cart is given to them and they can remove any unwanted items from their cart. In implementing these four features, I was able to build a solid foundation for this event sourced system.
I architected my system to use the CQRS pattern. This gives my Akka actor system two distinct sides to it, each represented by its own top-level actor. The CommandActor and QueryActor are used to route all incoming commands or queries to the respective actors that handle them. Actors on the command side also create events that are then sent over an event bus to the actors on the query side, which then update their projections.
All of my events are stored in a Sqlite database using Akka’s Persistence plugin, while my projections are all stored as JSON in a separate Sqlite database. This makes queries very fast, as they just return the current state of the projection from the database.
To interact with this shopping cart system, I decided to expose it over a REST interface. In designing the REST interface, one of the first challenges I faced was answering this question: How do I get a meaningful response back from my command to the actor system so that I can provide an HTTP response?
Most of my experience with Akka was using messages where I did not need to get a response back. Akka provides the Ask method where you can send a message and wait for response back. If I used Ask to communicate throughout my actor system, I could easily get a response back, but I would be blocking my actors from handling other messages. There is also the possibility with using Ask that I never get a response back and I end up with an actor that can’t do anything. I had to come up with a way to get a response while trying not to cripple the performance of my actors.
The solution I reached was using a combination of the Ask and Forward methods along with the Sender property. When processing messages, each actor has access to Sender property, which is a reference to the actor who sent the message. This allows actors to send back messages to the sender if needed. Using Sender.Tell is how the Ask method receives its response. The Forward method is the same as using Tell but doesn’t not change the Sender reference.
All of this enabled me to Ask commands to my CommandActor from my REST controllers. My message then gets sent down to the actor that handles it using Forward. This allows my handling actor to use Sender.Tell to give my response back to my awaiting REST controller. This keeps my actor system from blocking when communicating from actor to actor, but I am still able to give a response from my actor system to my REST controllers.
To help explain further, the diagram below shows the full path for adding a product to a cart, starting from the request from the client, through the actor system, and back to the client as an HTTP response.
The client makes a request to the REST API to add a product to the user’s cart. That request is then made into an AddProductToCart command that is sent to the Cart System. The Cart System then uses Ask to send the command and wait for a response.
The CommandActor gets the command, knows that the CartCoordinatorActor handles these commands, and forwards it on. The CartCoordinatorActor then finds the CartActor for the user specified in the command and forwards the command to them.
Validation of the command is done inside the CartActor and if it is valid, an event is created and sent to the Event Bus. A CommandResult is created, detailing whether the command was handled or not, and then sent back to the CartSystem using Sender.Tell().
This result is then sent back to the REST controller and turned into and HTTP response for the client.
One other challenge I had to solve was how to validate commands so that users have the correct restraints on what they can do in the application. For example, a user should not be able to add an item to their cart if none are in stock.
For this phase of development, a user could only have one of each product in their cart. This gave me three different levels of validation I needed to do. First, I had to make sure the command had all the necessary information. Second, there needed to be an item in stock to be added to the cart. Finally, I needed to make sure the product was not already in the user’s cart. Since there are different types of validation needed, I was not sure where to put all the code to handle validation.
The first and third levels of validation are very simple and straightforward. The first level of validation is done by the controllers in the REST API when they create the commands. The third level of validation happens in the Cart domain object that the CartActor contains. The Cart contains all of the business logic on what a Cart can and cannot do. It will throw an exception if the product cannot be added.
The second level of validation is the part I struggled with the most. I needed to get the inventory state to see if I could add the item to the cart. To do that, I had the CartActor ask the InventoryQueryActor if a product is in stock. At first, this seemed a little odd to me, as I did not think there should be a crossing of boundaries between the command and query sides. However, the query side is a representation of the current state of the system, so it makes sense to use my query actors information to validate commands.
In the few months that I have used Akka.NET, I have greatly expanded my knowledge on CQRS, Event Sourcing, and actor systems. I have worked through some challenges already but there are still many challenges this design brings. Now that I have a working system, I have many more ideas on how I want to expand this design.
I was able to get a response back to the client when a command was handled, but I have no way of knowing when the projections I want are updated. This is where I want to try using WebSockets to push my projections down to the client when they are ready.
I also only have a few actors that I use in this application, and I have not been pushing the limits of the system at all. I want to create a real-time chat application similar to Slack and really stress it to see how the actors handle many messages coming at once.
There are other aspects of Akka that I have not tried yet, such as creating actor systems across machines on a network. I still have so much I can learn with Akka, and I am excited to see how my application grows as I learn more.
You can see all of my code and watch my progress on Github.
If you have experience or more thoughts about Akka.NET, comment below!
Andrew Smith wrote this blog for us after completing his 2017 Q4 goal to learn more about Akka.NET. Andrew is a vital member of the EduSource team—he was one of the company’s very first apprentices, and has been a full-time employee since 2016. He’s a quiet guy, but he gets the more subtle points of humor—he’s usually laughing at his desk while the people around him are having ridiculous conversations.
If you want to know more about Andrew or any EduSource Employee, check out Meet Our Team.