Quickstart: Customer Registry in Scala

Learn how to create a customer registry in Scala, package it into a container, and run it on Akka Serverless.

Before you begin

If you want to bypass writing code and jump straight to the deployment:

  1. Download the source code using the Akka Serverless CLI: akkasls quickstart download customer-registry-scala

  2. Skip to Package and deploy your service.

Writing the Customer Registry

  1. From the command line, create a directory for your project.

    mkdir customerregistry
  2. Change into the project directory.

    cd customerregistry
  3. Download the build.sbt file

    curl -OL https://raw.githubusercontent.com/lightbend/akkaserverless-java-sdk/main/samples/scala-customer-registry-quickstart/build.sbt
  4. Create the sbt project directory

    mkdir project
  5. Download the plugins.sbt

curl -L https://raw.githubusercontent.com/lightbend/akkaserverless-java-sdk/main/samples/scala-customer-registry-quickstart/project/plugins.sbt -o project/plugins.sbt

Define the external API

The Customer Registry service will create or retrieve a customer, including their email, phone number and mailing address. The customer_api.proto will contain the external API your clients will invoke.

  1. In your project, create two directories for you protobuf files, src/main/proto/customer/domain and src/main/proto/customer/api.

    Linux or macOS
    mkdir -p ./src/main/proto/customer/api
    mkdir -p ./src/main/proto/customer/domain
    Windows 10+
    mkdir src/main/proto/customer/api
    mkdir src/main/proto/customer/domain
  2. Create a customer_api.proto file and save it in the src/main/proto/customer/api directory.

  3. Add declarations for:

    • The protobuf syntax version, proto3.

    • The package name, customer.api.

    • Import google/protobuf/empty.proto and Akka Serverless akkaserverless/annotations.proto.

      syntax = "proto3";
      package customer.api;
      import "google/protobuf/empty.proto";
      import "akkaserverless/annotations.proto";
  4. Add the service endpoint

    service CustomerService {
      option (akkaserverless.service) = {
        component: "customer.domain.Customer"
      rpc Create(Customer) returns (google.protobuf.Empty) {}
      rpc GetCustomer(GetCustomerRequest) returns (Customer) {}
  5. Add messages to define the fields that comprise a Customer object (and its compound Address)

    message Customer {
      string customer_id = 1 [(akkaserverless.field).entity_key = true];
      string email = 2;
      string name = 3;
      Address address = 4;
    message Address {
      string street = 1;
      string city = 2;
  6. Add the message that will identify which customer to retrieve for the GetCustomer message:

    message GetCustomerRequest {
      string customer_id = 1 [(akkaserverless.field).entity_key = true];

Define the domain model

The customer_domain.proto contains all the internal data objects (Entities). The Value Entity in this quickstart is a Key/Value store that stores only the latest updates.

  1. Create a customer_domain.proto file and save it in the src/main/proto/customer/domain directory.

  2. Add declarations for the proto syntax, Akka Serverless annotations, and domain model:attribute: value

    • The package name, customer.domain.

    • The Java outer classname, CustomerDomain.

      syntax = "proto3";
      package customer.domain;
      import "akkaserverless/annotations.proto";
  3. Add the option that the sbt plugin will use to generate the Customer Value entity, which will be of type customers and whose state will be defined in a CustomerState message:

    option (akkaserverless.file).value_entity = { (1)
      name: "Customer"
      entity_type: "customers"
      state: "CustomerState"
  4. Add the CustomerState message with fields for entity data, and the Address message that defines the compound address:

    message CustomerState {
      string customer_id = 1;
      string email = 2;
      string name = 3;
      Address address = 4;
    message Address {
      string street = 1;
      string city = 2;
  5. Run sbt compile from the project root directory to generate source classes in which you add business logic.

    sbt compile

Create command handlers

Command handlers, as the name suggests, handle incoming requests before persisting them.

  1. If it’s not open already, open src/main/scala/customer/domain/Customer.scala for editing.

  2. Modify the create method by adding the logic to handle the command. The complete method should include the following:

      override def create(currentState: CustomerState, customer: api.Customer): ValueEntity.Effect[Empty] = {
        val state = convertToDomain(customer);
      def convertToDomain(customer: api.Customer): CustomerState =
          customerId = customer.customerId,
          email = customer.email,
          name = customer.name,
          address = customer.address.map(convertToDomain)
      def convertToDomain(address: api.Address): Address =
          street = address.street,
          city = address.city
    • The incoming message contains the request data from your client and the command handler updates the state of the customer.

    • The convertToDomain and convertAddressToDomain methods convert the incoming request to your domain model.

  3. Modify the getCustomer method as follows to handle the GetCustomerRequest command:

    override def getCustomer(currentState: CustomerState, getCustomerRequest: api.GetCustomerRequest): ValueEntity.Effect[api.Customer] =
      if (currentState.customerId == "") {
        effects.error(s"Customer ${getCustomerRequest.customerId} has not been created.");
      } else {
    def convertToApi(customer: CustomerState): api.Customer =
        customerId = customer.customerId,
        email = customer.email,
        name = customer.name
    • If that customer doesn’t exist, processing the command fails.

    • If the customer exists, the reply message contains the customer’s information.

    • The convertToApi method converts the state of the customer to a response message for your external API.

      The src/main/scala/customer/Main.scala file already contains the required code to start your service and register it with Akka Serverless.

Package and deploy your service

To compile, build the container image, and publish it to your container registry, follow these steps

  1. From the root project directory, compile the source code using sbt:

    sbt compile
  2. Use the deploy task to build the container image and publish it to your container registry. At the end of this command sbt will show you the container image URL you’ll need in the next part of this quickstart.

    sbt docker:publish -Ddocker.username=[your-docker-hub-username]
  3. Sign in to your Akka Serverless account at: https://console.akkaserverless.com/

  4. If you do not have a project, click Add Project to create one, otherwise choose the project you want to deploy your service to.

  5. On the project dashboard click the "+" next to services to start the deployment wizard

  6. Choose a name for your service and click Next

  7. Enter the container image URL from the above step and click Next

  8. Click Next (no environment variables are needed for these samples)

  9. Check both Add a route to this service and Enable CORS and click Next

  10. Click Finish to start the deployment

  11. Click Go to Service to see your newly deployed service

Invoke your service

Now that you have deployed your service, the next step is to invoke it using gRPCurl

  1. From the "Service Explorer" click on the method you want to invoke

  2. Click on "gRPCurl"

  3. In the bottom section of the dialog, fill in the values you want to send to your service

  4. In the top section of the dialog, click the "Copy to clipboard" button

  5. Open a new command line and paste the content you just copied