Building a basket micro-service using ASP.NET Core and Akka.NET

Paul Nieuwenhuis
9 min readOct 20, 2016

--

Introduction

I’m working a lot with micro-services lately at an e-commerce company who is building a micro-services platform. These services are build on a powerful combination: Scala and Akka. Especially Akka changed my thinking about building high speed, scalable and resilient services, because of the actor based model and passing messages between them. Although I’m using Scala as the language of choice here, I’m also highly interested with the direction Microsoft is taken with .NET Core. What I really would like is to use .NET to build a ‘perfect’ micro-service and use a Docker container as distribution platform.

To be close to my e-commerce background, I’ll be building a simple basket service using ASP.NET Core version 1.0.1 and an unofficial alpha release of Akka.NET. Right now (as of 18 October) the contributors of Akka.NET are working on adding .NET Core support to this great port of Akka. Next release with full support will probably be version 1.5, but the version I’m using is surprisingly stable although I’m using a little bit of the functionality that is available. For example, DI support for .NET Core is not ready yet, but I’ll expect that to be complete when it releases. More information on Akka.NET can be found here: http://getakka.net/.

UPDATE: .NET Core 2.0 and Akka.NET 1.3 with official .NET Core support are released. I’ve updated my project.

The basket service I’m building only supports the basics: viewing the basket and adding/removing items to it. It also has an internal sample product catalog, containing a few (future) gaming consoles :-). All is in-memory and is lost after the application stops.

Because ASP.NET Core is cross-platform, i don’t want to use a development tool which is only available on Windows. So I’ll be using my command line console in combination with Visual Studio Code, which is a great combination in my opinion! Visual Studio Code can be downloaded here: https://code.visualstudio.com. Don’t forget to install the C# extension for a better development experience!

Check the final version of this solution on GitHub, as I’ll not describe each file in detail here. Full sample location: https://gitlab.com/pnieuwenhuis/newhouse-basket-service

Solution setup

Base solution structure

I’ll first start with basic directory structure. I’ve created an application under src/BasketService using the command: dotnet new. Furthermore, there is a global.json file, which defines the different projects in this solution. I will need this when tests are added.

The project.json under src/BasketService marks the directory as a .NET Core project and contains the following contents:

src/BasketService/project.json

Worth noting here is the version of Akka.NET, which is 1.2.0-alpha1. This unofficial version of Akka.NET is not hosted in the official NuGet repository. Therefore a ‘nuget.config’ is added to the root of the repository to locate this package. Also, I’ll be using Serilog for better and nicer logging in the console. The website for Serilog with more information can be found here: https://serilog.net/.

The ‘Startup.cs’ file contains the initialization of the service. Here, the logging and DI services will be initialized, but the Akka actor system will be created here too. The actor system will be added as singleton to the build-in DI container of ASP.NET Core:

Product domain

After this set-up, I’ll first start with the ‘product’ domain. As a good micro-service manages it’s own data, it should also have a subset of the product catalog available so it isn’t dependent on other services for this. The subset only includes data needed for the basket-service to operate and enough data for an eventual UI application which is consuming this micro-service. In this example, the product catalog is hard-coded though :-).

src/BasketService/Products/Product.cs

First, the product domain object should be created. As you can see, it contains only some basic data about the product, but not information like a full description, product type or product properties as it is not needed for this service.

The product catalog will have an API endpoint to query the full catalog:

src/BasketService/Products/Routes/ProductApiController.cs

The file only contains the minimal code to describe each endpoint. To keep code cluttering to a minimum, all actions will be separate classes which get injected here. The real implementation of ‘GetAllProducts’ will be done in an other file:

src/BasketService/Products/Routes/GetAllProducts.cs

Here you can see the first invocation to an actor called ProductsActor, where it sends the ‘GetAllProducts’ message and expects to receive a list of products. The usage of async and await makes this call fully asynchronous, so the application can handle other requests while waiting for the actor to return a result. This class gets the logger but also a provider injected into the constructor. The provider will create the ProductsActor and return a IActorRef reference. The provider also sets the product catalog that this actor is being initialized with. For now, to make all injections work I’ve created a file ‘Services.cs’ under src/Products and add registrations to the DI container there. With this, I keep it nicely organized without cluttering the ‘Startup.cs’ with all DI registrations.

src/BasketService/Products/Services.cs

As you can see, it is an extension method on IServiceCollection, so in ‘Startup.cs’ i can add this line: services.AddProductServices()

Now that routing is in place, it’s time to create the ProductsActor to handle product related requests. The ProductsActor has the Akka ReceiveActor as it’s base class and in the constructor it receives the list of products which has to be held in memory. The actor would be a singleton, so it loaded only once during the whole lifetime of the application. The actor supports a set of messages which can be send to the actor such as the ‘GetAllProducts’ message as you’ve seen above. These message definitions are separated to a different file from the ProductsActor to prevent clutter. It is still part of the ProductsActor class though by marking it as partial class.

src/BasketService/Products/ProductsActor.Messages.cs

Here you can see that the GetAllProducts object doesn’t even has an implementation because no other data is needed for the message. The other message UpdateStock does have some properties to allow the caller to give some detailed information about the action he want’s to be performed.

src/BasketService/Products/ProductsActor.Events.cs

The same also applies to the events that are returned to the caller by the ProductsActor. These are also simple POCO objects, sometimes containing extra information about the event that is performed by the actor. These events are also defined in a separate file from the actual actor implementation.

All events are inheriting from ProductEvent which only makes it easier for the caller to expect an event instead of a generic object.

The ProductsActor is a fairly simple actor because it does not persists events or changes behaviour (i.e. state machine). The first message to process is the GetAllProducts message, which can be handled directly in the Receive method. It returns only the in-memory list of products. Note that it is an immutable copy of the list, but with references to each product in the original list. So it is not fully immutable, because in that case, I had to clone each object… If only C# has more support for immutable cloneable structures like ‘case classes’ in Scala. Maybe ‘record types’ could help us in the future: https://github.com/dotnet/roslyn/blob/features/records/docs/features/records.md.

Implementation of GetAllProducts in ProductsActor

The second message is more complex, because there is some business logic involved here. Therefore the whole implementation is in a separate function:

UpdateStock message implementation in ProductsActor

Here you can see that it is returning the different event object instances (StockUpdated, InsuffientStock and ProductNotFound), based on the result. Using this, the caller can determine what happened and perform action on that (or not).

Basket domain

The basket implementation has the same setup as the product domain. So domain objects, messages, events and API implementation are all there and implemented in the same manner. You should check GitHub for how this was implemented.

I’d like to zoom into the implementation of adding items to the basket, because the BasketActor teams up with the ProductsActor for retrieving the needed product information.

Here you can see it asks the ProductsActor to update the stock first, by using a reference to the ProductsActor which it received in the constructor of the actor. It then uses the result that is returned from the ProductsActor to do it’s own actions and return it’s own result. I’m using a large ‘if…else’ construct here, but pattern matching would be a better idea. Luckily, this feature will be introduced in C# 6! Until then, this contructs helps to determine the result that was returned from the ProductsActor.

Also noteworthy is that we are returning an asynchronous operation as function result, because ‘asking’ the ProductsActor is also asynchronous. Therefore, in the constructor the Receive method must also be asynchronous:

Using ReceiveAsync and the PipeTo(Sender) method makes sure that the asynchronous result is send directly to the sender of the message.

Another thing to note is that when creating a new basket item, the product data is copied over from the product object, instead of adding a reference to the product in the basket item. The reason of doing this, is to prevent mixing up models from different domains. Now I have a snapshot of the product at the time it was added, and it cannot be changed when the product data is changing.

OK, so we have one basket actor with an in-memory list of it’s items, but what if there are multiple customers (which is hopefully the case on a e-commerce site)? To solve this, not all messages from the API layer are directly send to the BasketActor, but an intermediate actor called BasketsActor. This actor reads the customer identifier from the message that is sent and then forwards the message to the correct BasketActor.

Implementation of the message forwarder of the BasketsActor

It will create the actor first if it does not exist in the actor system. So the BasketsActor has a collection of child actors, where each actor is a basket of a customer. The child actor is identified using the customer identifier, so for example the identification of a basket of customer 12 in the actor system is: /user/baskets/12.

Final thoughts

Please check out my GitHub repository for the full implementation, because not all is covered here! The repository is located here: https://gitlab.com/pnieuwenhuis/newhouse-basket-service

As an user of the original Akka on Scala, I really dig the fact that Akka.NET is really close to the functionality of his bigger brother. Kudos to the team for that! In this blog, I’ve just scratched the surface of what is possible with Akka, but my goal was to have a functional service that is running on ASP.NET Core. Please note that ASP.NET Core support for Akka.NET is still in progress!

Next step would be using Docker to deploy this onto a micro-services platform and use it in combination with other micro-services. Examples of this can already be found elsewhere, such as here: https://medium.com/trafi-tech-beat/running-net-core-on-docker-c438889eb5a

My implementation here proves that it is possible to build high-performant and resilient services on the .NET platform and deploy it in a micro-services landscape.

--

--

Paul Nieuwenhuis

Full Stack Developer with interest for micro-services, building web applications and CI/CD.