Although you might (should) want to start your application as a monolith first, I would like to suggest that you should decouple your back-end from the front-end from the start.
A split front-end and back-end stacks is a good middle ground between a monolith and microservices. The main benefit of this approach is flexibility of changing either of the stacks when the need arises.
There are costs of maintaining separate stacks, I'll highlight them below. But after working in few projects that uses “one stack/framework to rule them all”, the benefits outweigh the costs in my opinion.
The flexibility problem
One of the projects that I am involved with is written with Grails. As a typical MVC framework, Grails is responsible for both back-end and front-end.
A requirement came from the client to implement multi-tenancy. Sounds like a reasonable request, but the team faced one roadblock after another. Yes, we are aware of Grails multi-tenancy guide, but we couldn't get that to work with our application.
Honestly, this is one of the many frustrating things that we found working with Grails. The team came into conclusion that re-writing the application in another framework / language would be better for the future. A more popular framework like Ruby on Rails that is suited for rapid application development with a strong open-source community sounds like a good fit for us (I admit I am biased).
However we couldn't easily move forward with the re-write as the effort to re-write the software would be simply too much for us to tackle at this point. Were the software was implemented using the split stack approach, we could stagger the re-write. Assuming we are happy with our front-end stack and Grails as the back-end is the thing that we want to replace - we could gradually swap the APIs with a different backend framework.
I never realised the benefit of having that flexibility until now.
Image from here, original image from Dinosaur Comics
As with most things in software, choosing one approach over the other is about trade-offs.
The future-proofing afforded by the flexibility comes with costs, here are some of them:
- Additional burden of maintaining two ecosystems. Both ecosystems have their own package management, version manager, testing framework, servers (the SPA needs a simple rendering server too - separate to the API server), deployment methods, build pipelines .. I should stop now.
To put it simply, the cost of flexibility is the increase in complexity.
Architect your software for flexiblity from the start makes sense, I feel the flexibility offered is just too hard to ignore, but be prepared to handle the increase in complexity.