DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
  1. DZone
  2. Coding
  3. Java
  4. Maven, SBT and Modularisation

Maven, SBT and Modularisation

Jan Machacek user avatar by
Jan Machacek
·
Nov. 27, 12 · Interview
Like (0)
Save
Tweet
Share
5.11K Views

Join the DZone community and get the full member experience.

Join For Free

Let me revisit the Maven to SBT post, and expand on the modularisation of Akka applications. I will also outline the Akka AMQP addition, which allows you to use AMQP brokers like my favourite RabbitMQ in your Akka applications. Again, it is a long post, so gather some food & drink to make it all the way to the end.

Maven to SBT

The first thing to tackle is the source code modularisation. How do we build & deploy our application and how do our choices influence the packaging and building approaches. I shall start with typical Maven strcutrue. Imagine a typical Akka application that constructs the important actor structure in the core module, operating on the instances of classes in the domain. We then expose the functionality in the api and expose the API on HTTP in the web module. To support our efforts throughout the code, we may even have the test module, which helps us write code in the tests.

In a typical Maven beast, you’d create these modules (domain, core, …) as separate modules, with explicitly defined dependencies on the other modules. The reasoning is that this ensures that you don’t skip tiers; and that you can reuse portions of your application’s code. (Assuming you have some sort of company-wide artifact repository.)

On the face of it, this sounds good. But my experience with large project tells me that this almost never works as well as you think. The clear dependencies between the modules become tangled; and often the strict “separate project” structure prevents large refactorings. Finally, you almost always deploy the entire application, packaged up in, say a WAR. Our reasons for using multi-module Maven projects are no longer that relevant; and in most cases, they actually hurt us.

Out with Maven!

This is the line you’ve no doubt been waiting for. Out with Maven, in with SBT; and while we’re at it, let’s build single-module SBT project. We’ll build a small Akka application whose small portion connects RabbitMQ to deliver & route the messages. Naturally, we’ll include tests, including tests that exercise the AMQP componentry.

First of all, all our source code will sit in the src directory. Because we also need some SBT plugins, we’ll create the project directory. We will complete the picture with the build.sbt file; along with a few good housekeeping files. We need something like this:

$ ls -la
-rw-r--r--  1 janmachacek  staff  1155 17 Nov 16:42 README.md
-rw-r--r--  1 janmachacek  staff  2389 17 Nov 16:42 build.sbt
drwxr-xr-x  2 janmachacek  staff   102 11 Nov 17:00 project
drwxr-xr-x  4 janmachacek  staff   136 11 Nov 17:00 src
-rw-r--r--  1 janmachacek  staff    31 11 Nov 17:00 version.sbt

Turning to build.sbt, we have the dependencies and other bits and pieces. Here it is in its entirety:

import sbtrelease._

/** Project */
name := "Akka Patterns"

version := "1.0"

organization := "org.cakesolutions.akkapatterns"

scalaVersion := "2.10.0-RC2"

/** Shell */
shellPrompt := { state => System.getProperty("user.name") + "> " }

shellPrompt in ThisBuild := { state => Project.extract(state).currentRef.project + "> " }

/** Dependencies */
resolvers += "spray repo" at "http://repo.spray.io"

resolvers += "Typesafe Repository" at "http://repo.typesafe.com/typesafe/releases/"

resolvers += "Sonatype OSS Releases" at "http://oss.sonatype.org/content/repositories/releases/"

resolvers += "Sonatype OSS Snapshots" at "http://oss.sonatype.org/content/repositories/snapshots/"

libraryDependencies <<= scalaVersion { scala_version => 
  val sprayVersion = "1.1-M5"
  val akkaVersion  = "2.1.0-RC2"
  Seq(
    "com.typesafe.akka" % "akka-kernel"       % akkaVersion cross CrossVersion.full,
    "io.spray"          % "spray-can"         % sprayVersion,
    "io.spray"          % "spray-routing"     % sprayVersion,
    "io.spray"          % "spray-httpx"       % sprayVersion,
    "io.spray"          % "spray-util"        % sprayVersion,
    "io.spray"          % "spray-client"      % sprayVersion,
    "io.spray"          % "spray-json"        % "1.2.2" cross ...,
    "org.mongodb"       % "mongo-java-driver" % "2.9.3",
    "com.aphelia"      %% "amqp-client"       % "1.0",
    "io.spray"          % "spray-testkit"     % sprayVersion % "test",
    "com.typesafe.akka" % "akka-testkit"      % akkaVersion  % "test" cross ...,
    "org.specs2"        % "classycle"         % "1.4.1" % "test",
    "org.specs2"        % "specs2"            % "1.12.2"     % "test" cross ...
  )
}

/** Compilation */
javacOptions ++= Seq("-Xmx1812m", "-Xms512m", "-Xss6m")

javaOptions += "-Xmx2G"

scalacOptions ++= Seq("-deprecation", "-unchecked")

maxErrors := 20 

pollInterval := 1000

logBuffered := false

cancelable := true

testOptions := Seq(Tests.Filter(s =>
  Seq("Spec", "Suite", "Unit", "all").exists(s.endsWith(_)) &&
    !s.endsWith("FeaturesSpec") ||
    s.contains("UserGuide") || 
    s.contains("index") ||
    s.matches("org.specs2.guide.*")))

/** Console */
initialCommands in console := "import org.cakesolutions.akkapatterns._"

Nothing too funky. Let’s take a look at our src directory, which contains the entire source code, split into the main and the test codebase. We run the main code on the server and we run the test when we want to check that the main does the right thing. The packages we create in the bowels of the src directory mimicks our Maven modules:

$ ls -la
total 8
drwxr-xr-x  9 janmachacek  staff   340 17 Nov 16:42 .
drwxr-xr-x  3 janmachacek  staff   102 10 Nov 13:06 ..
drwxr-xr-x  2 janmachacek  staff   238 17 Nov 16:42 api
drwxr-xr-x  4 janmachacek  staff   272 17 Nov 16:42 core
drwxr-xr-x  2 janmachacek  staff   272 17 Nov 16:42 domain
drwxr-xr-x  2 janmachacek  staff   136 17 Nov 16:42 main
drwxr-xr-x  2 janmachacek  staff   102 17 Nov 16:42 test
drwxr-xr-x  2 janmachacek  staff   102 17 Nov 16:42 web

This structure requires more discipline; you’re free to tangle your packages. Instead of relying on the rigid structure of our Maven build (or the multi-project SBT build), we will include tests that verify the architectural soundness of our codebase. We shall be using Specs2, so the test that verifies our architecture should be no surprise. (Note that you’ll need to compile & run the code using JDK 1.7.)

class ArchitectureSpec extends Specification 
  with Analysis with ClassycleDependencyFinder {

  "The architecture" should {
    "Have properly defined layers" in {
      val ls = layers(
        "main",
        "web",
        "api",
        "core",
        "domain"
      ).withPrefix("org.cakesolutions.akkapatterns").
        inTargetDir("target/scala-2.10")

      ls must beRespected
    }
  }

}

So, instead of having separate Maven modules and having some of our architectual constraints expressed in the modules’ dependencies, we have moved our architectural tests to our testing code.

Deploying & running the modules

If our project includes just a single main method, all that you have to do to boot your project is to type sbt run and you’re up!

Suppose you now have two main components in your applicaiton; and you connect these two components over AMQP. So, we now have two main methods. Trying to execute sbt run will ask which main you want to execute? So, instead of sbt run, we’ll need to say sbt run-main org.cakesolutions.akkapatterns.main.Server to boot the core actors and sbt run-main org.cakesolutions.akkapatterns.Keystore to run the key store component.

Even though we can control which component to run, we always deploy the entire codebase.

I’ll give you the details of the AMQP-based actor communication in the next few days.

Apache Maven

Published at DZone with permission of Jan Machacek, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • Microservices Discovery With Eureka
  • Real-Time Stream Processing With Hazelcast and StreamNative
  • Express Hibernate Queries as Type-Safe Java Streams
  • API Design Patterns Review

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: