What is Play framework?
It may be surprising to write about Play framework in the year 2022 for many developers familiar with JVM-based solutions, its community, and all the code base created since the beginning of time when the dinosaurs were walking the earth, but I believe Play framework is still a very valuable tool in every developer’s toolbox and should be considered as a solution to multiple problems.
Even though a lot of developers I know would prefer to work with purely FP-based solutions like Http4s/Circe/Doobie stack, I still think that the speed of creating CRUD-type applications with Play framework cannot be ignored and can come in handy, especially in today’s very popular microservices world.
What exactly is Play framework? If you remember the times when we had a boom of multiple web frameworks, among those some big ones like Ruby on Rails and Django or ASP.NET MVC, you could notice that the internet was full of information on what an MVC pattern is, and how it solves all the world’s problems :). Play framework was actually a response to that need in the JVM ecosystem. Play is an MVC-based web framework with some additional good stuff borrowed from other implementations (especially RoR), like convention over configuration and hot reloading capability.
When the first stable version of Ruby on Rails appeared around 2005, Play's first version was created more than 3 years later, officially released in 2009 as Play 1.0.
At some point, in 2013, the Play framework was considered the most popular project on Github, and although the development slowed down significantly since that time, it is still a very active project with a new version released every couple of months.
Number of commits per year since 2011:
The image below depicts the basic flow in the Play application from the development point of view. When I look for an HTTP endpoint and what it does I always start from the routes file, the nice thing about Play is that it has all the endpoints nicely organised in a single file and you don’t need to look for HTTP endpoint definitions in multiple places by searching for strings etc. The single route definition looks like the following:
GET /generatedreports controllers.GeneratedReportController.getGeneratedReports(page: Long ?= 1L, itemsPerPage: Long ?= 10L)
First, you specify the HTTP method, second the path (with path parameters if needed) and then the exact method in your controller where you can pass the path parameters and also add some more which will be bound automatically as query your query params.
The controller method used in the routes file needs to have the same signature and return Action (either sync or async based on Future).
With most of the Play applications I have worked with, the Controller is simply responsible only for handling requests, eventual form binding, calling appropriate service class method and translating the service return type to the appropriate response. So generally we try to keep them clean and simple.
The service class is the place where all the business logic happens. This is the place where we mostly call databases (with the help of additional repository classes), external services etc. and execute the proper business logic of our application.
The nice thing is that we can create all of our service and data access layers with effects (like Cats IO) and only call IO.unsafeToFuture() inside our controller to translate it back to our old and not-so-good Future type.
This explanation of the basic logic flow in Play application is very simplified, but I hope anyone who hasn’t used Play before will get the feeling of navigating the basic Play project.
Selected Play framework features
Play framework is a full-blown web framework and you probably can find any feature you want either built-in or as an additional plugin, right below I will list the subset of the basic features I think are nice to have out of the box:
- Injection - this is a no-brainer for all the people who were using Play in the past but I want to mention it as one of the very nice things to have out of the box when building an app quickly. It allows you to very fast connect your dependencies instead of building modules, passing your dependencies through multiple constructors, resource initializations etc. as you do in the modern Http4s/Cats effect approach
- Configuration - Under the hood play uses the Typesafe config library, so you can simply place your config in typical
application.conf, but it also has a special wrapper around it called
Configurationwhich you can inject anywhere and use wherever you want
- Templating - I guess this one thing built-in is the major one for me. It's pretty easy to just use Play as a microservice base and have that functionality available, for example when you want to expose an admin interface to your microservice or send an HTML email using the template. The templates themselves are very powerful, I won’t go into details here but of course, all the standard templating stuff is available (like if-else, iterations, includes etc) Templating examples
- Forms and form validation as well as multipart form handling - out of the box you can bind your request to a form object, enhanced with validation and Cross Site Request Forgery (CSRF) protection built in Form examples
- Database handling with Slick and evolutions
- Testing - Play supports Specs2 and Scalatest out of the box, you can test your controllers with routes, services and DB layer. There are a few useful helper classes you can use to make testing easier. More on testing can be found here
- Many more…
At times when you have a ton of libraries and frameworks to choose from when developing a new web-based solution, why exactly would you choose such an ‘old’ one over other ones?
In my opinion Play framework has many advantages which are especially very valuable when you develop microservices with a need for a simple web interface to monitor or administer its operations.
From the scala developer's pov:
- Easy and fast to set up
- Once you get used to conventions used it gets pretty easy to navigate within the code base
- Built-in features like evolutions, caching, data layers etc
- Multiple starter projects available with 0auth, basic crud functionality etc
- Works with scala and java so no need to use anything else
- You can base all your business logic on tagless final and/or IO
- Easy to test with extensive documentation for testing controllers, services, repositories
- Template engine, nice routing, built-in security, filters and many more
Depending on your needs, there will be times when using Play won’t be the best option and that is ok. You need to consider all pros and cons and make a decision yourself:
- Play is a mature project and it's based on Akka which sometimes can be a bliss or a curse
- Many projects related to Play are no longer maintained, the authors after many years of supporting new versions just stopped doing so
- The future is unknown but the project itself is still being maintained, alive and kicking
Rounding it up
As you can see, despite a long time on the market, the Play framework is alive and kicking. With a single line of code, you can enable modules for caching, evolutions, HTTP clients and many more. I think it can be a valuable tool in your toolbox, especially when in need of creating a microservice with some templating capabilities.