Scala Tutorial - Real-world build.sbt

By Nadim Bahadoor | Last updated: March 5, 2020 at 12:44 pm

Overview

The previous tutorial was a great start to get you up and running with the basic building blocks that typically make up a build.sbt file. In particular, it is common for the IntelliJ IDEA code editor to define a build.sbt file whenever you create a New Scala Project. As for the allaboutscala Scala project, the build.sbt file would most certainly be as follows:


name := "allaboutscala"
version := "1.0"
scalaVersion := "2.13.0"

While this is surely good enough for experimenting with Scala, real-world production Scala applications tend to require a bit more creativity. You generally use the build.sbt file to: (1) import or add relevant artifacts or dependencies, (2) define and apply various settings, and (3) define particular project or projects. As a matter of fact, we will refactor the default build.sbt file to mirror the above-mentioned, such that it is logically split into the following sections: artifacts, settings, and projects. Therefore, go ahead and delete all the default properties - that is, name, version and scalaVersion - leaving you with basically a blank build.sbt file!

 

Steps

1. Define the artifacts section in build.sbt

In the empty build.sbt file, we start by defining a closure using lazy val artifacts = new { ... }, and it will encapsulate all the required artifacts, or dependencies, of our Scala project. Within this closure, we capture the versions for the artifacts, or dependencies as shown below. This helps to quickly identify, or update, artifact versions at some later point in time.


// ARTIFACTS or DEPENDENCIES
lazy val artifacts = new {

  // artifacts versions
  val scalaV            = "2.13.0"
  val realworldAllaboutscalaV = "1.0"
  val akkaActorV        = "2.5.23"
  val akkaHttpV         = "10.1.8"
  val akkaHttpTestkitV  = "10.1.8"
  val scalaLoggingV     = "3.9.2"
  val logbackV          = "1.2.3"
  val scalaTestV        = "3.0.8"
  val pureconfigV       = "0.11.1"
  
}

Let us assume that we create a new project named  realworld-allaboutscala application, and that this particular project will require the following artifacts: (1) Akka HTTP for bootstrapping and exposing our REST endpoints, (2) scala-logging for general logging purposes, (3) scalatest so that we can write tests for our application, and (4) pureconfig to easily wire-in any necessary configuration properties. Hence, we sum up the relevant artifacts using corresponding Seq collection so as to facilitate adding these to the libraryDependencies section.


// ARTIFACTS or DEPENDENCIES
lazy val artifacts = new {

  // artifacts versions
  val scalaV            = "2.13.0"
  val realworldAllaboutscalaV = "1.0"
  val akkaActorV        = "2.5.23"
  val akkaHttpV         = "10.1.8"
  val akkaHttpTestkitV  = "10.1.8"
  val scalaLoggingV     = "3.9.2"
  val logbackV          = "1.2.3"
  val scalaTestV        = "3.0.8"
  val pureconfigV       = "0.11.1"

  // artifacts
  val akkaHttp = Seq(
    "com.typesafe.akka" %% "akka-actor" % akkaActorV,
    "com.typesafe.akka" %% "akka-stream" % akkaActorV,
    "com.typesafe.akka" %% "akka-http" % akkaHttpV,
    "com.typesafe.akka" %% "akka-http-spray-json" % akkaHttpV,
    "com.typesafe.akka" %% "akka-testkit" % akkaActorV % "test",
    "com.typesafe.akka" %% "akka-http-testkit" % akkaHttpTestkitV % "test"
  )

  val scalaLogging = Seq(
    "com.typesafe.scala-logging" %% "scala-logging" % scalaLoggingV,
    "ch.qos.logback" % "logback-classic" % logbackV
  )

  val scalatest = Seq("org.scalatest" %% "scalatest" % scalaTestV % "test")
  val pureconfig = Seq("com.github.pureconfig" %% "pureconfig" % pureconfigV)
}

It goes without saying that your particular enterprise, or company-wide project, will include similar or equally relevant artifacts such as the ones above. As a general note, you may find that in real-world production applications, and especially those with a micro-services viewpoint, the above artifacts closure may be shared across other subsequent Scala projects, and even so exposed as a custom sbt plugin. A gentle reminder to always verify the version compatibility among your various artifacts by going through their respective documentations, as opposed to merely searching for artifacts from Maven Central. This is particular true within an enterprise setting, which may have its own internal repository in the likes of Artifactory or Nexus Repository.

 

2. Define the settings section in build.sbt
The second section of our build.sbt file defines a number of lazy val to capture sbt settings that may be common, or shared, across projects. For instance, the lazy val commonSettings identifies the particular scalaVersion, or logLevel, that would generally be identical across, say, different Scala projects that make up a micro-service, or lambda, architecture. Consider further the importance of having consistent scalacOptions - also referred to as compiler flags - to avoid any haphazard behaviors across related projects. It is also customary to have similar sbt test settings and, for that matter, we define a lazy val testSettings.


// SETTINGS
lazy val commonSettings = Seq(
  organization          := "com.realworld.allaboutscala",
  scalaVersion          := artifacts.scalaV,
  version               := artifacts.realworldAllaboutscalaV,
  logLevel              := Level.Info,
  scalacOptions ++= Seq(
    "-encoding", "UTF-8",
    "-Xfatal-warnings",
    "-deprecation",
    "-feature",
    "-unchecked",
    "-language:implicitConversions",
    "-language:higherKinds",
    "-language:existentials",
    "-language:postfixOps",
    "-Ywarn-dead-code"
  )
)

lazy val testSettings = Seq(
  fork in Test := false,
  parallelExecution in Test := false,
  libraryDependencies ++= artifacts.scalatest
)

3. Define the project section in build.sbt
Our Scala Single Project is called realworld-allaboutscala and, likewise, we define a lazy val realworldAllaboutscala = (project in file(".")). And, where file(".") locates the root directory of the project as being equivalent to the location of the build.sbt file. Next, we can start chaining the required .settings for our Scala Single Project as shown below.


// PROJECT or PROJECTS
lazy val realworldAllaboutscala = (project in file("."))
  .settings(name:= "realworld-allaboutscala")
  .settings(commonSettings: _*)
  .settings(testSettings: _*)
  .settings(libraryDependencies ++= artifacts.scalaLogging ++ artifacts.akkaHttp ++ artifacts.pureconfig)
  .settings(resolvers += Resolver.sonatypeRepo("releases"))
  .settings(resolvers += Resolver.sonatypeRepo("snapshots"))

You will observe that we are making use of the earlier commonSettings, testSettings and artifacts. Given that most enterprises have internal repositories, we illustrate how you can attach equivalent resolvers that will locate your corresponding artifacts. In our example, however, the artifacts will be downloaded from the Maven Central repository. Having a similar pattern, structure or organization within the main building blocks of your build.sbt file is actually primordial in enterprise Scala projects. Perhaps an even compelling reason is that most Scala projects would rarely be designed as a huge monolithic application. Instead, and with micro-services in mind, and with the obvious benefits of easily scaling your software, applications that are designed as the ones above can very easily be translated in Scala Multi-Project setup. If you are just beginning with Scala, and have no prior industry experience - that is of course OK! - and that is why you are perhaps here on my blog :) The good news is that with Scala Multi-Project setup, you can very easily sharereuse, and develop Scala code across multiple teams. You can find a lot more step-by-step and real-world approach to building Scala Multi-Project with my book  - Scala For Beginners -  where I show you how to build such a Scala multi-project that is quintessential to most enterprise platforms by having various parts of the system encapsulated in small, and reusable, projects.

 

This concludes our tutorial on Scala Tutorial - Real-world build.sbt and I hope you've found it useful!

 

Stay in touch via Facebook and Twitter for upcoming tutorials.

 

Don't forget to like and share this page :)

Summary

In this article, we went over the following:

  • Define the artifacts section in build.sbt
  • Define the settings section in build.sbt
  • Define the project section in build.sbt

Tip

  • You can find additional tutorials on, say, using Akka HTTP through my blog, and also step-by-step instructions to develop a real-world Scala Multi-Project through my book - Scala For Beginners.

Source Code

The source code is available on the allaboutscala GitHub repository.

 

What's Next

In the next section, I will show you IntelliJ's Settings screen which is important to get familiar with as it will help you with every aspect of building Scala applications.

 

Stay tuned!

Nadim Bahadoor on FacebookNadim Bahadoor on GithubNadim Bahadoor on LinkedinNadim Bahadoor on Twitter
Nadim Bahadoor
Technology and Finance Consultant with over 14 years of hands-on experience building large scale systems in the Financial (Electronic Trading Platforms), Risk, Insurance and Life Science sectors. I am self-driven and passionate about Finance, Distributed Systems, Functional Programming, Big Data, Semantic Data (Graph) and Machine Learning.
Other allaboutscala.com tutorials you may like: