Overview

This article was also published on the site: https://dzone.com/articles/a-small-micro-service-developed-in-scala-using-hex.

Scala is a language that I have been using extensively in my work, but with the focus on Big Data using Spark for data processing.

Scala was in my playlist of languages that I wanted to delve into a bit more and not only experiments for the area of Big Data using Spark, but to write a micro service using http and rest for studies.

The idea is to apply the concept of Hexagonal Architecture (ports and adapters). My micro-service is called a Sparrow Account, and the source code is available in GitHub. I also did a similar experiment in using the Clojure language that you can see in GitHub.

Let’s go to the requirements.

1. Micro-Service Requirements

Create a micro-service that checks and creates a hypothetical bank account.

It must comprise a HTTP Server with two endpoints:

Requirements:

Proposed Solution:

The architecture of the proposed solution follows the Hexagonal Architecture concept. The design is based on two books:

Example Diagram of a Hexagonal Architecture:

Organization Application Package Diagram:

1.1 Http Rest Server

To build a request and response Http Rest Server, Finagle-Finch was used:

Piece of code where the server is used: src/main/scala/sparrow/account/ServerApp.scala

def runServer(): Unit = {
  val app = Http
    .server
    .withLabel(serverConf.name)
    .withAdmissionControl.concurrencyLimit(
    maxConcurrentRequests = serverConf.maxConcurrentRequests,
    maxWaiters = serverConf.maxWaiters
  ).serve(s"${serverConf.host}:${serverConf.port}",
  (Routes.balanceAccount :+: Routes.fillAccount).toService)
  onExit {
    app.close()
  }
  Await.ready(app)
}

The EndPoints available on the server:

Method EndPoint Example Parameter
POST /account {“uuid”:”1”, “amount”:100.50}
GET /balance not required

Piece of code of the routes with the EndPoints: src/main/scala/sparrow/account/ServerApp.scala

final val fillAccount: Endpoint[Account] =
  post("account" :: jsonBody[AccountFillRequest]) {req: AccountFillRequest =>
    for {
      r <- accountService.fillAccount(req.uuid, req.amount)
    } yield r match {
      case Right(a) => Ok(a)
      case Left(m) => BadRequest(m)
    }
  }

There are two EndPoints fillAccount that can create an account and deposit a value, as well as can withdraw using the negative value. And the EndPoint balanceAccount to see the balance available to the user.

1.2 Transactional Memory and Concurrency Control

When we are talking about micro-service, we have to guarantee the atomicity of the code, so that no undue competition occurs. To control concurrency the ScalaSTM was used.

Piece of code where atomicity is used: src/main/scala/sparrow/account/controller/AccountController.scala

override def fillAccount(uuid: String, amount: Double): Future[Either[AccountFillException, AccountTransaction]] = Future {
  if (accounts.get(uuid).isEmpty) createAccount(uuid, 0)

  accounts.get(uuid) match {
    case Some(transact) => {
      atomic {implicit tx =>
        transact() = AccountTransaction(transact().uuid, transact().amount + amount)

        displayOperationType(transact().uuid, transact().amount)

        if (amountIsNegative(transact().amount))
          transact() = AccountTransaction(transact().uuid, transact().amount - amount)

        Right(transact())
      }
    }
    case _ => Left(AccountFillException("Fill account not found."))
  }
}

1.3 Other Tools Used

Other tools used in the project are in the order below:

1.4 Building the Project

To build the project can follow the instructions in the own repository of the git hub located here:

Conclusion

We saw in this short article how easy and simple it is to create a micro-service application using Scala language. It could also have used other libraries such as the AKKA that I predict will soon make an article about it.

Thanks!