Backend Actor logic

In this example, the backend uses only one basic actor. In a real system, we would have many actors interacting with each other and perhaps multiple data stores and microservices.

An interesting side-note to add here is: using actors in applications like this adds value by just providing functions that returns Futures. In fact, if your logic is stateless and simple request/reply style, you may not need to back it with an Actor. Actors do shine however when you need to keep some form of state and allow various requests to access something in (or through) an Actor. The other stellar feature of actors, which futures would not handle, is scaling-out onto a cluster very easily, by using Cluster Sharding or other location-transparent techniques.

However, the focus of this tutorial is on how to interact with an Actor backend from within Akka HTTP – not on the actor itself, so we’ll keep it very simple.

The sample code in the UserRegistryActor is straightforward. It keeps registered users in a Set. Once it receives messages it matches them to the defined cases to determine which action to take:

package $package$;

import akka.actor.*;
import akka.event.Logging;
import akka.event.LoggingAdapter;
import akka.japi.Creator;

import java.util.*;

public class UserRegistryActor extends AbstractActor {

  LoggingAdapter log = Logging.getLogger(getContext().getSystem(), this);

  public static class User {
    private final String name;
    private final int age;
    private final String countryOfResidence;

    public User() {
      this.name = "";
      this.countryOfResidence = "";
      this.age = 1;
    }

    public User(String name, int age, String countryOfResidence) {
      this.name = name;
      this.age = age;
      this.countryOfResidence = countryOfResidence;
    }

    public String getName() {
      return name;
    }

    public int getAge() {
      return age;
    }

    public String getCountryOfResidence() {
      return countryOfResidence;
    }
  }

  public static class Users{
    private final List<User> users;

    public Users() {
      this.users = new ArrayList<>();
    }

    public Users(List<User> users) {
      this.users = users;
    }

    public List<User> getUsers() {
      return users;
    }
  }
  

  static Props props() {
    return Props.create(UserRegistryActor.class);
  }

  private final List<User> users = new ArrayList<>();

  @Override
  public Receive createReceive(){
    return receiveBuilder()
            .match(UserRegistryMessages.GetUsers.class, getUsers -> getSender().tell(new Users(users),getSelf()))
            .match(UserRegistryMessages.CreateUser.class, createUser -> {
              users.add(createUser.getUser());
              getSender().tell(new UserRegistryMessages.ActionPerformed(
                      String.format("User %s created.", createUser.getUser().getName())),getSelf());
            })
            .match(UserRegistryMessages.GetUser.class, getUser -> {
              getSender().tell(users.stream()
                      .filter(user -> user.getName().equals(getUser.getName()))
                      .findFirst(), getSelf());
            })
            .match(UserRegistryMessages.DeleteUser.class, deleteUser -> {
              users.removeIf(user -> user.getName().equals(deleteUser.getName()));
              getSender().tell(new UserRegistryMessages.ActionPerformed(String.format("User %s deleted.", deleteUser.getName())),
                      getSelf());

            }).matchAny(o -> log.info("received unknown message"))
            .build();
  }
}

If you feel you need to brush up on your Akka Actor knowledge, the Getting Started Guide reviews actor concepts in the context of a simple Internet of Things (IoT) example.