Design & Architecture: Framework Coupling
Can I press delete button on your Spring, Guice, Hibernate, JPA dependencies and still be able to test and use your business features? If not, you might have a huge problem – high framework coupling.
Why Is It So Important?
Firstly, frameworks and libraries age much quicker than our software does. We want to upgrade them as often as possible and change them as easily as possible. In 3 years from now, the current style of writing Spring applications might be totally obsolete. Or we might want to shift to another cool framework out there. Remember all those EJB applications people wrote in the past? Most of these still exist, someone has to maintain and develop them. Worse, probably right now there are many people rewriting those in rage to Spring or Jooby. And they’re probably making the same mistake.
Secondly, and this is something even more harmful, high framework coupling leads to untestability. Each extra framework you couple to, you have to take into account when testing, making testing process more complex, often a lot longer and, in extreme cases impossible.
Microservices Don’t Change Anything
One of the arguments I heard against considering framework coupling a problem is that we write microservices. That doesn’t change anything. No matter what high-level architecture you choose, you will probably write similar amount of code to cover all the business features. What’s the difference between having 1 huge poorly designed application and having 20 small poorly designed applications? Each of these is maybe more manageable, but you have to test, release and monitor 20 instead of 1.
Then I was told that each of these applications is so small that it’s easy to rewrite it. Wrong way to go. If I have a dozen microservices, each taking 3 months to write and I want to rewrite each every 3 years, I’m getting into an endless loop of rewriting! And no, it probably won’t go faster when “just rewriting”, because in 3 years it won’t be the same team any more – they will have to learn things from scratch or from framework-coupled code.
Background
I first came across this problem in Uncle Bob’s post Screaming Architecture. I looked around my projects and saw a bunch of Spring “services”, repositories and configuration classes. They all scream Spring. Then I started thinking it out and realized it’s more than just class names. This is the thing that made me lose a lot of nerves and hours in the past – when I wanted to upgrade framework’s version, delete one completely from the system, test something in a framework-coupled application or read code written years ago in company’s internal frameworks.
Solution
The solution is easy, but uncomfortable for most developers. You have to change the way you write your applications.
Set clear boundaries for your business components. Make sure none of these boundaries are violated by framework dependencies. There is a great video about architecture and boundaries on Clean Coders:Architecture, Use Cases, and High Level Design.
Whenever you want to use some of the framework capabilities you should invert the dependency (Dependency Inversion Principle). Sometimes, it may require some extra classes, e.g., interfaces or adapters, but it’s really worth it. This also applies to JPA and other persistence mechanisms. You should never use persistence data structures like business objects – data structures and objects are different by definition!
Once you have all of the dependencies inverted, you might want to put business related code into separate physical component, i.e., separate JAR file. Physical boundaries are an ultimate way of protection against unwanted dependencies – your code won’t compile unless you pull them in.
What should seem natural at this point, you should strive to keep your test suite as framework-independent as possible. And I don’t mean frameworks like JUnit or Cucumber here. I mean that your business code should be 100% testable without using any of the frameworks your non-business components use, like Spring or Hibernate.
Another, more general rule that should be followed is: Keep your frameworks easy to use and easy to remove.Even if you probably won’t change all the frameworks in your application, it is more than sure that you will want to upgrade their version. Make it pleasure of using newer technologies, not pain of dealing with the old ones.
Credit: Grzegorz @ tidyjava.com