Understanding Hello World

§Understanding Hello World

After creating and running Hello World from the command line, you no doubt appreciate what Lagom framework did for you. There was no need to determine what infrastructure you might need and then install and configure it. The template removed the necessity to set up a project or build structure. And, as you create services of your own, Lagom detects changes and performs a hot reload! Lagom allows you to concentrate on satisfying your business needs.

The separation of concerns illustrated in Hello World and an introduction to service descriptors and the registry will help you as you start developing your own microservices:

§Service interface

The service interface belongs in the api project. For instance, the service interface for the hello service resides in the hello-api project (look for the HelloService.scala source file).

import akka.Done
import akka.NotUsed
import com.lightbend.lagom.scaladsl.api._
import play.api.libs.json._

trait HelloService extends Service {
  def hello(id: String): ServiceCall[NotUsed, String]

  def useGreeting(id: String): ServiceCall[GreetingMessage, Done]

  final override def descriptor = {
    import Service._
    named("hello")
      .withCalls(
        pathCall("/api/hello/:id", hello _),
        pathCall("/api/hello/:id", useGreeting _)
      )
      .withAutoAcl(true)
  }
}

case class GreetingMessage(message: String)

object GreetingMessage {
  implicit val format: Format[GreetingMessage] = Json.format[GreetingMessage]
}

Note that:

  • The service interface inherits from Service and provides an implementation of Service.descriptor method.

  • The implementation of Service.descriptor returns a Descriptor. The HelloService descriptor defines the service name and the REST endpoints it offers. For each endpoint, declare an abstract method in the service interface as illustrated in the HelloService.hello method. For more information, see Service Descriptors.

§Service implementation

The related impl project, hello-impl provides implementation for the service abstract methods. For instance, the HelloServiceImpl.scala source file contains the service implementation of the HelloService.hello method for the hello service. The service implementation uses a sharded, persistent, typed actor providing data persistence using Event Sourcing and CQRS.

import akka.actor.typed.ActorRef
import akka.actor.typed.Behavior
import com.lightbend.lagom.scaladsl.api.ServiceCall
import akka.cluster.sharding.typed.scaladsl.ClusterSharding
import akka.cluster.sharding.typed.scaladsl.EntityRef

import scala.concurrent.ExecutionContext
import scala.concurrent.duration._
import akka.util.Timeout
import com.lightbend.lagom.scaladsl.api.transport.BadRequest

class HelloServiceImpl(clusterSharding: ClusterSharding)(implicit ec: ExecutionContext) extends HelloService {
  implicit val timeout = Timeout(5.seconds)

  override def hello(id: String): ServiceCall[NotUsed, String] = ServiceCall { _ =>
    entityRef(id)
      .ask[Greeting](replyTo => Hello(id, replyTo))
      .map(greeting => greeting.message)
  }

  override def useGreeting(id: String) = ServiceCall { request =>
    entityRef(id)
      .ask[Confirmation](
        replyTo => UseGreetingMessage(request.message, replyTo)
      )
      .map {
        case Accepted => Done
        case _        => throw BadRequest("Can't upgrade the greeting message.")
      }
  }

  private def entityRef(id: String): EntityRef[HelloWorldCommand] =
    clusterSharding.entityRefFor(HelloWorldState.typeKey, id)
}

Found an error in this documentation? The source code for this page can be found here. Please feel free to edit and contribute a pull request.