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.
Create a micro-service that checks and creates a hypothetical bank account.
It must comprise a HTTP Server with two endpoints:
Requirements:
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:
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.
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."))
}
}
Other tools used in the project are in the order below:
To build the project can follow the instructions in the own repository of the git hub located here:
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!