Play 2.1 makes it possible to write completely modular applications, and on top of this, to develop each module separately. I’ve been waiting for this for some time now, and even implemented a custom router based on plugins while waiting for the framework to support modular routing. Now that the wait is over, let’s see how to take advantage of this feature.

The Play documentation describes how to set-up a multi-modular project via SBT sub-projects. What is important to understand here is that:

  • sub-projects are a feature of SBT, and next to having a central Build.scala file, it is possible to add configuration to individual sub-projects via build.sbt files (in those sub-project’s root directory)
  • the proposed folder structure (i.e. putting the modules in a modules/ directory) is optional (but a good convention nonetheless)
  • each sub-project is a play.Project, so routes and messages files in conf/ will be recognized

Let’s get started with creating a new project:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

manu@manu-mac ~/workspace » play new multi-module-example
_ _  
\_ __ | | \__ _ _ _| |  
| '_ \| |/ \_' | || |\_|  
| \_\_/|\_|\\\_\_\_\_|\\_\_ (_)  
|\_| |\__/

play! 2.1.0 (using Java 1.7.0_15 and Scala 2.10.0), http://www.playframework.org

The new application will be created in /Users/manu/workspace/multi-module-example

What is the application name? [multi-module-example] >

Which template do you want to use for this new application?

1 - Create a simple Scala application  
2 - Create a simple Java application

> 1  
OK, application multi-module-example is created.

Have fun!  

Now that we have created a parent project, let’s right away create a first module:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

manu@manu-mac ~/workspace » cd multi-module-example
  
manu@manu-mac ~/workspace/multi-module-example » mkdir modules  
manu@manu-mac ~/workspace/multi-module-example » cd modules  
manu@manu-mac ~/workspace/multi-module-example/modules » play new module1  
_ _  
\_ __ | | \__ _ _ _| |  
| '_ \| |/ \_' | || |\_|  
| \_\_/|\_|\\\_\_\_\_|\\_\_ (_)  
|\_| |\__/

play! 2.1.0 (using Java 1.7.0_15 and Scala 2.10.0), http://www.playframework.org

The new application will be created in /Users/manu/workspace/multi-module-example/modules/module1

What is the application name? [module1] >

Which template do you want to use for this new application?

1 - Create a simple Scala application  
2 - Create a simple Java application

> 1  
OK, application module1 is created.

Have fun!  

Now that we have the basic structure in place, let’s adjust the main’s project Build.sbt. At this point, we just want to make sure that our sub-module gets built, tested and packaged together with the main project. That is why we need to add the aggregate instruction to the main project. The projet/Build.scala build file should look like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import sbt._


import Keys._
import play.Project._

object ApplicationBuild extends Build {

 val appName = "multi-module-example"
 val appVersion = "1.0-SNAPSHOT"

 val appDependencies = Seq(
  // Add your project dependencies here,  
  jdbc,
  anorm
 )

 val module1 = play.Project("module1", path = file("modules/module1"))

 val main = play.Project(appName, appVersion, appDependencies).settings(
  // Add your own project settings here  
 ).dependsOn(
  module1
 ).aggregate(
  module1
 )

}  

Note that we do not any longer need the Build.scala of the module, i.e. the directory modules/module1/project can be deleted.

Now, what we need for our module to work properly, is to:

  • Set-up the routing correctly
  • Move the controller to the right package
  • Wire-up the routes of the sub-module into the parent’s router

First, let’s rename the modules/module1/conf/routes file to modules/module1/conf/module1.routes and adjust its content:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# Routes
  
# This file defines all application routes (Higher priority routes first)  
# ~~~~

# Home page  
GET /module1 controllers.module1.Application.index

# Map static resources from the /public folder to the /assets URL path  
GET /assets/*file controllers.module1.Assets.at(path="/public", file)  

Note how the generated Application controller is now moved to the module1 package (it needs to be adjusted in the modules/module1/app/controllers/Application.scala file as well), and the Assets controller is also part of the module1 package. Each sub-project needs its own Assets controller, if it should provide assets.

The Assets file looks like this:

1
2
3
package controllers.module1
  
object Assets extends controllers.AssetsBuilder  

Now, let’s plug-in the new router in the main’s project router, in the conf/routes file:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# Routes
  
# This file defines all application routes (Higher priority routes first)  
# ~~~~

# Home page  
GET / controllers.Application.index

-> / module1.Routes

# Map static resources from the /public folder to the /assets URL path  
GET /assets/*file controllers.Assets.at(path="/public", file)  

We can check that this works by accessing an undefined route:

Now that everything is in place, there is just one last thing to do in order to make the development a pleasurable experience. Indeed, it is possible to develop each module separately, and treat it as a separate application, by switching to this project in the SBT console:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
[multi-module-example] $ projects
  
[info] In file:/Users/manu/workspace/multi-module-example/  
[info] module1  
[info] * multi-module-example  
[multi-module-example] $ project module1  
[info] Set current project to module1 (in build file:/Users/manu/workspace/multi-module-example/)  
[module1] $ run

- (Running the application from SBT, auto-reloading is enabled) — [info] play - Listening for HTTP on /0.0.0.0:9000

(Server started, use Ctrl+D to stop and go back to the console...)  

However, this won’t work right away:

As we now switched to the module project, effectively making it the currently active Play project, the default application configuration kicks in. By default, the router is defined in the conf/routes file.

Luckily, there is a way to change this in the application.conf:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# Router
  
# ~~~~~  
# Define the Router object to use for this application.  
# This router will be looked up first when the application is starting up,  
# so make sure this is the entry point.  
# Furthermore, it's assumed your route file is named properly.  
# So for an application router like \`my.application.Router\`,  
# you may need to define a router file \`conf/my.application.routes\`.  
# Default to Routes in the root package (and conf/routes)  
# application.router=my.application.Routes  

Let’s adjust our module’s application.conf to do just this, and to also inherit from the parent’s configuration:

1
2
3
include "../../../conf/application.conf"

application.router=module1.Routes  

And that’s pretty much it, we can now work independently on our new module: