CRUD Microservice with AkkaHttp

Alexey Novakov
SE Notes by Alexey Novakov
5 min readNov 1, 2018

Let’s use Scala and its famous library AkkaHttp to build microservice for Create-Read-Update-Delete type of operations. We will briefly look at these libraries by I end of this article:

  1. AkkaHttp
  2. Slick
  3. Spray Json
  4. Cats
  5. Scala Logging
  6. Scala TestContainers
  7. Pure Config & Typesafe Config
  8. Docker Compose
  9. SBT

High-Level Stack

Building blocks

Application Layers

Service Components

Domain Models

Our domain model is quite simple. It consists of Trip, Trips (wrapper around sequence of the Trips), Vehicle enum for Trip and CommandResult for return type of the service.

REST API

We need the following methods to support CRUD operations:

  • Create: POST Trip JSON
  • Read all: GET Trips by sort, page, pageSize parameters
  • Read one: GET Trip by ID parameter
  • Update: PUT Trip JSON by ID in parameters
  • Delete: DELETE Trip by ID in parameters

Commands Routes

Let’s separate our endpoints into different AkkaHttp Routes. First route is for Commands, i.e. operation which ask to change a state.

Query Routes

Two more endpoints to read state of the Aggregate.

AkkaHttp Directives composition looks like a sieve for incoming HTTP request. When it first matches to one of the Directive, then the inner block is executed, this is similar to IF condition. If it does not match, then HTTP 404 code will be returned. As you can see above, the Directives DSL is human-readable, you can approximately understand what is going on.

Both routes have external dependency such as service: TripService[Future] variable. That means that the return types will be of type Future, so that AkkaHttp response Marshaller will translate into response. Basically, HTTP Routes delegate user calls to the TripService. Generic type of TripService[T] has nothing to do with AkkaHttp. It is just our internal implementation to abstract return type of the service.

JSON Codes

To make these Routers work we need to parse JSON request into Scala case classes. We will use spray-json library to parse and serialize case classes back to String. There is a code below to create JSON formatters for our case classes.

I omitted part where Vehicle Enum and LocalDate are formatted. It requires some additional coding. See full version here: https://github.com/novakov-alexey/akka-crud-service/blob/master/src/main/scala/org/alexeyn/JsonCodes.scala

However, all simple types and basic collections are supported by Spray-Json, so that Encoder/Decoders can be automatically derived.

Service — Business Logic

Service encapsulates all operations exposed by Command and Query REST API. We divided REST API into 2 routes in advance to have opportunity split this microservice into different application in case we want to apply CQRS pattern in full scale.

Our service does simple validation and returns Either.Left when something is wrong at the input parameters. It also delegates the operations to DAO (data access object) to make specific state changes in the database or read from it.

Functor Type Class

As you may noticed, we’ve used Functor Type Class from Cats library to abstract our return type, i.e. managing the effect. We need only ‘map’ function presence for the return type, so that Functor is exactly the case here. Usage of Functor will be useful later, when it comes to Unit Testing. Also, this may be useful when it comes to replacement of Scala Future, which we need to return to AkkaHttp, with some another effect class.

Persistence

First of all we create a trait which describes our DAO type. We abstract return type as well to be more flexible at Unit Tests and to have ability to change the actual DAO implementation in future. Our current DAO implementation will be based on Scala Future due to Slick library, however we could select another DB library and use another effect type later.

Implementation in Slick:

Combining All Parts

Let’s combine three pieces together, add Configuration loader and JSON formatters.

Project Tree

Application Configuration

PureConfig loader gives quite convenient way to load HOCON configuration into your case classes. We also return original instance of Typesafe/Lightbend Config instance for later usage in Slick API. Current configuration is very simple, there are only host and port of our HTTP server to bind to.

Wiring All Components

We do not use any Dependency Injection library, but just class constructors.

Note the usage of StrictLogging (another option is LazyLogging) trait at Module class above. It comes from scala-logging library. The nice thing of this library that it allows to mixin an instance of the logger into our classes, so that you do not need to create it manually.

Start HTTP Server

In Main class we create AkkaHttp server with internal guts like ActorSystem, ActorMaterializer. We borrow ExecutionContext for Scala Future to complete serverBinding future. There is single ExecutionContext used for non-blocking operations along the way of HTTP routes and service. However, Slick creates its own AsyncExecutor with thread pool suitable for blocking I/O like JDBC.

Run with Docker Compose

In order run our microservice and its database we can use docker-compose. First of all we need to build our microservice and wrap it into Docker image. We can do that very easy with SBT Native Packager plugin.

dockerBaseImage is set to Alpine Linux image with JRE8 included which is bare minimum to run our Scala microservice app. Also, we enable two plugins which package the whole project into a Zip Archive having auto-generated bash scripts. This scripts will be used by Dockerfile definition. Note there is no need to define Dockerfile manually or write any other scripts. Above two plugins are enough to produce runnable Docker image which will execute java with our main class and libraries in the CLASSPATH. Our docker-compose looks likes this:

version: '2.2'
services
:

akka-crud-service:
image: akka-crud-service:0.1.0-SNAPSHOT
container_name: akka-crud-service
mem_limit: 2g
environment:
APP_CONFIG_PATH: /opt/conf/application.conf
volumes:
- ./config/application.conf:/opt/conf/application.conf
ports:
- 8080:8080
depends_on:
- postgres

postgres:
restart: always
image: postgres:10.4
container_name: akka-crud-service-postgres
ports:
- 5432:5432
environment:
- POSTGRES_USER=trips
- POSTGRES_PASSWORD=trips
- POSTGRES_DB=trips

Summary

Nowadays Scala and its ecosystem provide mature variety of technologies to build any kind of service oriented architecture.

Anybody who is stuck with Java and its famous Spring Boot framework, I encourage you to try to build your next microservice using Scala. You will end up with 2–3 smaller codebase and safer program structure than it would be in Java.

Links

  1. GitHub source code: https://github.com/novakov-alexey/akka-crud-service
  2. Scala Logging: https://github.com/lightbend/scala-logging
  3. Akka Http: https://github.com/lightbend/scala-logging
  4. Slick: http://slick.lightbend.com/
  5. Cats: https://typelevel.org/cats/
  6. Pur Config: https://github.com/pureconfig/pureconfig

--

--