Spoiler Alert: There is a (mostly) happy ending. I was successful in converting my project using the the below tools. The project has been running in production for over a year now. The goal in this series is to share what I learned in the process and give you the information you need to decide for your own project whether or not to do the same.
Recently, I was looking around at potential replacements for the Play Framework, due to the steadily declining activity on the project and lack of updates. I was also reliant on the Silhouette security library whose author had also recently announced he was retiring the project. When Lightbend announced they were dropping the Play Framework and letting the community take it over, it was the last nail in the coffin for me.
Note: Both projects have since been picked up by the community, have had recent releases and are to some extent still alive.
My main criteria was that there be at least one compatible security module providing a foundation for things like OAuth2, JWT, authentication & authorization, etc. and that the framework and the security modules be widely used and actively maintained. Moving away from Play with Silhouette was going to be a big undertaking just to keep the lights on and I did not want to go through it all again in a year.
I also considered rolling my own micro-service framework using the various best of breed libraries available for things like the HTTP server, background tasks etc. built around an effects system like cats or zio. At the end of the day the majority of libraries needed to build such a system failed to meet the criteria mentioned above.
My eventual conclusion was that I would not find what I was looking for; something that resembled the Play Framework in it’s prime; a lightweight “Spring-esque” framework built for Scala projects. This raised the question: “Why not use the Spring Framework? with Scala?” Scala for the most part is compatible with Java libraries and it’s fairly common for Scala projects to have at least one such dependency due to the general lack of viable native options, so why not a framework? I decided I would give it a try.
Since I was going to have to go through a major rewrite anyway I decided to also drop Slick from my persistence layer, replace all Future usage with IO’s and swap out Play’s JSON libraries with Circe.
My initial setup:
- IntelliJ IDE
- Scala 3*
- Gradle – Mostly out of curiosity; I’ve never loved SBT and wanted to see what Gradle could do here. It also seemed like there could be additional benefits with Spring going into the mix.
- Spring Boot
- Spring Security
- Cats Effect 3 – I experimented briefly with ZIO and largely worked as expected, I found myself favoring the simplicity and focus of cats-effect. At the time of my investigations there were also many more libraries built on top of cats and I wanted to avoid using shims as much as possible.
- Doobie – As far as SQL libraries for Scala go, I had been running into the same kind of issues I was with my web framework; popular options were seemingly being abandoned (Slick) and while there were many alternatives, none looked particularly promising when it came to standing the test of time. I eventually settled on Doobie because of it’s native use of Cats Effect, and because the functional -> relational abstraction suffered from too many issues when it came to optimization and supporting various db extensions etc.
- Circe
- Scalatest
- Mockito
*Scala 3 proved to be a non starter: Issues ranged from incompatibilities in dependencies to weird quirks in IntelliJ’s Scala 3 support. I eventually got things to compile but concluded that sticking with Scala 3 (at least for the time being) was going to more of a hinderance than a help and reverted to 2.13.
Part 2 of this series will focus on general Java -> Scala challenges and quirks / etc. with the different layers of Spring:
- Spring Annotations in Scala
- Using ScalaTest with Spring MockMvc
- Background Tasks
- Jackson -> Circe
- Static Contexts
- Endpoint Mappings
- Endpoints -> Cats Effect concerns
- Using Circe instead of Jackson
- Using Doobie for persistence