A Small Microservice Developed in Scala Using Hexagonal Architecture
A software developer explores how to create HTTP REST end-points for use in a microservice application using the Scala language and Hexagonal Architecture.
Join the DZone community and get the full member experience.
Join For FreeOverview
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 experiment with in the area of Big Data using Spark, but to write a microservice using HTTP and REST.
The idea is to apply the concept of Hexagonal Architecture (ports and adapters). My microservice is called a Sparrow Account, and the source code is available in GitHub. I also did a similar experiment using the Clojure language that you can see on GitHub.
Let’s go to the requirements.
1. Microservice Requirements
Create a microservice that checks and creates a hypothetical bank account.
It must comprise an HTTP Server with two endpoints:
- One to insert a new monetary transaction, money in or out, for a given user.
- One to return a user’s current balance.
Requirements:
- It must not be possible to withdraw money for a given user when they don’t have enough balance.
- You should take concurrency issues into consideration.
- Store data in memory.
- Use designer partners and good coding practices, like:
- Immutability.
- Separation of concerns.
- Unit and integration tests.
- API design.
- Error handling.
- Language idiomatic use.
- Use the functional programming paradigm.
Proposed Solution
The architecture of the proposed solution follows the Hexagonal Architecture concept. The design is based on two books:
Below is an example diagram of a Hexagonal Architecture:
Below is an diagram laying out the Organization Application Package:
1.1 HTTP REST Server
To build a request and response HTTP REST Server, Finagle-Finch was used:
Here's the 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 end-points available on the server are:
Method | EndPoint | Example Parameter |
---|---|---|
POST | /account | {“uuid”:”1”, “amount”:100.50} |
GET | /balance | not required |
Here's the piece of code of the routes with the end-points: 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 end-points: fillAccount
that can create an account and deposit a value, as well as can withdraw using the negative value; and the balanceAccount
end-piont to see the balance available to the user.
1.2 Transactional Memory and Concurrency Control
When we are talking about microservices, we have to guarantee the atomicity of the code, so that no undue competition occurs. To control concurrency the ScalaSTM was used.
Here's the 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, you can follow the instructions in the GitHub repository located here:
Conclusion
We saw in this short article how easy and simple it is to create a microservice application using the Scala language. It could also have used other libraries such as the AKKA that I predict will soon make an article about it.
Thanks!
Opinions expressed by DZone contributors are their own.
Comments