Bootzooka 2019: functional Scala and React

Adam Warski
SoftwareMill Tech Blog
7 min readAug 21, 2019

--

Bootzooka is a template project for a Scala-based microservice, or a Scala+React web application.

It implements some very basic functionality: user registration, login, profile management and password recovery, along with all the infrastructure that’s needed:

  • integrated SBT+Webpack build
  • unit & integration tests
  • SQL database access
  • HTTP API
  • email sender
  • various deployment options

The goal is to provide all the boring parts so that you can quickly jump into the development of your project.

Bootzooka contains a minimal, but fixed and opinionated set of libraries; if you like our choices — great! If you prefer other libraries for some of the functionalities — minor adjustments shouldn’t be a problem.

However, it’s good to keep in mind that Bootzooka is only one out of many possibilities to write a backend Scala and frontend React application. It’s possible that you might want to take an entirely different approach.

You can try out Bootzooka live, browse the source code, or read the documentation.

Let’s take a quick look at what’s inside!

The backend

The backend is based on the “functional” Scala stack, that is:

These libraries don’t depend on each other (and hence can be easily swapped). They interoperate thanks to cats-effect, which defines a set of typeclasses used by Monix, http4s, tapir and doobie.

The guiding principle for Bootzooka’s backend is:

No magic. Usage of every component and implementation of every functionality (user-facing or infrastructural) should be navigable from the Main class, using go-to-definition.

Below you’ll find a couple of Bootzooka’s features and design decisions explained in more detail.

Per-feature modules

Functionalities are grouped into packages. Each package contains a FooModule trait, which defines the wiring for the classes contained in that package (usually using lazy vals), and expresses dependencies on classes from other packages (usually using defs). For example:

In other words, this implements “no-framework” dependency injection, using plain constructors. See the di-in-scala guide for more details.

Case-class based configuration

pureconfig uses the popular typesafe-config file configuration format — using .conf files — but makes it possible to read configuration directly into case classes. This saves a lot of code!

Moreover, the configuration is logged on startup, making it much easier to debug configuration-related problems.

Declarative HTTP API endpoint descriptions, Swagger UI

Bootzooka uses http4s as the HTTP server, however API endpoints are described using tapir. Such descriptions can be interpreted as an http4s HttpRoute object, or as documentation.

An example endpoint description looks as follows:

Bootzooka also exposes the documentation for all endpoints using the Swagger UI:

All of the API and admin endpoints are collected and exposed, along with the documentation routes, in HttpApi. That’s also where the CORS, CorrelationId and metrics http4s middlewares are applied.

The API is exposed as http://localhost:8080/api/v1 and the docs as http://localhost:8080/api/v1/docs.

Correlation id support

Speaking of correlation ids; thanks to Monix’s support for Task-local variables, it’s possible to implement correlation ids transparently, without the need to thread it through all the method calls. See this blog for details.

The correlation id is read from the X-Correlation-ID header, or a new one is generated when the request starts. It is then added to all messages that are logged when processing the request:

The yellow part is the correlation id for the request. All messages logged during the processing of the request have the same id. Emails are sent asynchronously, in a background process, so they don’t have the correlation id set.

Moreover, the correlation id is added as a header to all outgoing HTTP requests, which are done using the configured sttp backend.

Single-import to use Doobie/JSON/HTTP

It’s quite easy to forget the exact set of imports needed to make Circe’s auto-derivation work as needed, or to import all of the doobie’s meta-instances so that custom types are supported.

That’s why Bootzooka groups all these functionalities into three objects. Each such object extends a set of traits from the appropriate library and defines implicits to support custom types. For example:

Then, if you’d like to use JSON serialization/deserialization, just import the Json object:

To create SQL queries, similarly, import the Doobie object. To describe HTTP endpoints, import all members of the http: Http instance.

Type-safe ids and lower-casing

If you take a look at the Json example, you notice that Bootzooka uses type tagging (e.g. Id @@ User). Type tags are a convenient way to create a “subtype” of a given type (here the base type is Id), and to tag it with any other type (here: User).

The tags are only used at compile-time. At run-time, they are erased and only the base type is used. This technique has been introduced in shapeless, but here a stand-alone variant is used.

When working with the database it’s common to juggle various ids, and they easily get mixed up. Tagging them with the type of the entity to which they correspond to, makes this much less error-prone, as it’s checked at compile-time!

The same technique can be used for differentiating between arbitrary and lowercased strings:

Metrics, admin endpoints

Along with regular API endpoints, Bootzooka exposes administration endpoints. The first are Prometheus metrics, which contain:

  • the JVM process metrics
  • HTTP server metrics (provided by http4s)
  • outgoing HTTP calls metrics (provided by sttp)
  • custom metrics (defined in the Metrics class)

The second admin endpoint exposes version information. This might be very useful when the application is deployed to various environments to quickly verify which version of the application (git hash) we are dealing with.

Composable and safe side-effect management

Bootzooka uses lazily-evaluated value wrappers, thanks to which working with side-effects is concurrency-, transaction- and type-safe. The two basic datatypes are Monix’s Task, which describes arbitrary side effects, and Doobie’s ConnectionIO, which describes database operations.

They offer a number of combinators to sequence, handle errors, allocate resources and run effects asynchronously. For example, the following describes a sequence of database operations, which will later be run in a single transaction:

Thanks to referential transparency, code can be refactored without fear. Extracting common logic into a method or a value won’t change the behavior of the application, even if the refactored fragment contains (wrapped) side-effects.

Simple manipulation of deeply nested data structures

Using quicklens, we can modify nested case classes without the need for multiple .copy invocations:

The frontend

Bootzooka’s UI layer consists of a few views, including homepage, login & register forms, as well as secured sections (available for logged-in users only).

The stack

We’ve started the original Bootzooka with Angular.js 1.x, and it served the open-source community quite well for the last few years. Over time, however, we’ve noticed a great shift towards React (which, as it also turned out, most of our developers prefer over the “new” Angular), so with the newest release, we’ve decided to re-write the frontend part as well.

Bootzooka’s UI is a relatively small project, so we’ve decided to stay with JavaScript, but as TypeScript is becoming more and more popular every day, we don’t rule out a typed version of this code to be released in the future.

Tools

The project has been bootstrapped with the fantastic Create React App, which “hides” all of the cumbersome details (like build configuration, minification & hot reloading) under the hood, exposing few simple scripts, while still allowing the developer to highly customize the settings of the project when needed. Create React App has become the de facto industry standard — long gone are the days of writing long & complicated webpack configurations for the most basic tasks.

We also prefer using yarn package manager over npm (hence the presence of the yarn.lock file).

Libraries & stack

We are aware of the rapidity of changes in the trends of the frontend world, so our goal was to keep the stack as small as possible. Application’s state management, for instance, has been implemented using React’s context API, instead of Redux (or similar). This solution has a transparent structure and should be easy to replace with a library of choice.

Same goes for styling — with a variety of solutions like SCSS or CSS-in-JS we’ve stayed with plain old CSS. Again — easy to customize, highly uncoupled.

For more details, check out Bootzooka’s manual.

Deployment

There are three deployment options available in Bootzooka out-of-the-box. The first is to create a fat jar, which contains both the backend and frontend code. Run:

sbt backend/assembly

Invoking java -jar backend/target/scala-2.12/bootzooka.jar will start the application on the configured host/port. Environmental variables can be used to customise configuration, as specified in application.conf.

Another option is to create a Docker image:

sbt backend/docker:publishLocal

This will create a bootzooka image locally. It can be started, together with a database, using the provided docker-compose.yml file.

Finally, you can deploy the application to Heroku. Just follow the documentation!

Looks great! How do I start using Bootzooka?

To start using Bootzooka, simply clone or fork the repository. Next, you might want to change the name! There’s a utility for that, run:

sbt "renameProject com.mycompany myproject"

And you should have a fully renamed project, waiting for new functionalities!

The documentation might be helpful to find your way in the structure of the code.

This post was written together with Marcin Baraniecki, our frontend expert at SoftwareMill.

--

--

Software engineer, Functional Programming and Scala enthusiast, SoftwareMill co-founder