I followed the documentation and created the top project, then subprojects. Then created dependencies. Btw, if you want to generate the eclipse projects from console make sure you do not aggregate the projects. For some reason, whenever I add aggregate(subproj1, subproj2) to the top level poject in build.sbt it will omit some projects.
All good, projects compile, and run. But now it is important to take a look at the packages - make sure you refactor your code and move all controllers in subproj1 into their own package (ie. controllers.subproj1). Then, do the same for the scala.html files in views. You should end up with something like this:
multiproj build.sbt app controllers Application.java views index.scala.html main.scala.html conf application.conf routes public images ... stylesheets ... javascripts ... modules core build.sbt app controllers.core Application.java views.core index.scala.html main.scala.html conf application.conf core.routes public ... test build.sbt app controllers.test Application.java views.test index.scala.html main.scala.html conf application.conf core.routes public ...
and the top build.sbt looks like this:
name := "multiproj" version := "1.0-SNAPSHOT" libraryDependencies ++= Seq( javaCore, cache ) play.Project.playJavaSettings lazy val root = project.in(file(".")).dependsOn(core, test) lazy val core = project.in(file("modules/core")) lazy val test = project.in(file("modules/test")).dependsOn(core)
and the top routes file:
GET / controllers.Application.index() -> /core core.Routes -> /test test.Routes # Map static resources from the /public folder to the /assets URL path GET /assets/*file controllers.Assets.at(path="/public", file)
Obviously, the core and test routes files must use proper packages when referring to the controller classes. So far, it is not that bad. But what about the resources? Notice how the same top level public folder is invoked above. Well, there is an extra step - create an Assets class in the controllers.core and controllers.test that will help scope the lookup to the project directory. Play documentation nicely provides the contents of this class:
package controllers.test; import play.api.mvc.Action; import play.api.mvc.AnyContent; public class Assets { public static Actionat(String path, String file) { return controllers.Assets.at(path, file); } }
and now update the route in the test.routes accordingly:
GET /assets/*file controllers.test.Assets.at(path="/public", file)
Make change in the stylesheet from top level project and you will see it applied. Now change the color in the stylesheet from test project and nothing happens. WTF?!?!
Well, as it turns out, Play uses the same classloader to load the files, so if the file name from top level and subproject is the same, it is not going to work. Thus, it is important that the assets from subprojects (public folders from subprojects) have unique names. You can follow the same scheme as for routes file and have public/main.css in top level project and public/test.main.css in the test subprojects. Then you will need to make sure you use the proper reverse routing in the template. Example: @controllers.test.routes.Assets.at("stylesheets/test.main.css")
Bottom line, yes you can split your application in sub-projects and be organized, but I am a bit disappointed in the number of hoops I have to jump to make it go. It should be simpler, Play!. Please make it more straight-forward! I would suggest that the console should know about sub-projects and pre-create the right structure and naming... Can't be that hard to make the console do all the renaming steps above.
Links:
- http://www.playframework.com/documentation/2.2.x/SBTSubProjects
- https://github.com/playframework/playframework/issues/1181