What are the differences between building on top of a framework and building on top of an application? How does using an application as a framework cause problems, and how can these problems be avoided? That is what this post is all about.
Decoupled web application
In your typical web application, the code handles a request and returns a response. Let's assume we are using a web framework to handle common tasks such as routing. Let's also assume that we think framework binding has a high cost and decouple our application from it. The flow of control would look like this:
Execution starts with the framework. For PHP frameworks this will be in a file like
public/index.php. The framework then bootstraps itself and does a bunch of stuff. It's safe to assume this stuff will include routing, and often it also includes things like dependency construction and error handling.
After the framework did the tasks you want it to do, it hands control over to your application. Your application does a bunch of application and domain logic and interacts with persistence. It likely uses a number of libraries, especially for infrastructure tasks like logging and database access. Even so, control stays with the application. (The key difference between frameworks and libraries is that you control/call libraries while frameworks control/call you.) Your application might also be calling the framework and use it as a library. Again, control stays with the application.
Finally when the application is done, it hands some kinda of result back to the framework. The framework then does another bunch of stuff, like template rendering and translations. In case of a web framework it then spits out a HTTP response and execution ends.
An application like this keeps you in control of what happens, making it easier to change things. This style also makes it easy to decouple from the framework. There are only two points where you need to decouple.
My post Implementing The Clean Architecture outlines one architectural approach that leads to this kind of application.
Frameworks vs Applications
Let's compare how frameworks and applications differ when they are used as a foundation for an/another application.
Frameworks don't do stuff on their own. There is no application or domain logic. There is no set of existing web pages or API endpoints with their own structure and behavior. This is all defined by your application when using a framework. When building on top of an application that acts as a framework, you'll need to deal with existing structure and behavior. You'll need to insert your own stuff, change existing behavior in certain situations and prevent default behavior altogether in others.
I know that there are "frameworks" that do provide their own stuff out of the box. (Example: web shop framework.) While they might not be a full application on their own, for the purpose of this blog post they are the same as an application that gets used as a framework.
Plugins and Extensions
There is nothing inherently bad about building things on top of an application. Plugins and extensions are a very useful pattern. A plugin that interacts with a single plugin point can decouple itself when appropriate and is in control over itself. And for extensions that use many extension points of the application yet are shallow/small, framework decoupling might not make sense.
This post is about using applications as framework foundation for sizable sets of code which are applications in their own right.
Applications as Frameworks
Let's imagine we have an application that is used on some site for some use case. We'll call this application FrameworkApp, since we'll use it as framework for another application that powers another site.
When building our application on top of FrameworkApp, we'll need to register new behavior and modify existing behavior. To make this possible, FrameworkApp needs to provide the appropriate extension points. Often these take the form of abstract classes or even systems, though the exact nature of the extension points is not important for our purposes.
This leads to a very different flow of control. Rather than calling us once, the FrameworkApp calls each extension point our application handles.
The diagram is showing just 6 extension points, though there can be 100s.
When visualized like this, it becomes easy to see how decoupling from the framework becomes next to impossible. Even if you manage to avoid coupling to framework code in your application, its whole structure is still defined by the framework. This means you are very limited in what you can do in your application and need to understand the framework to effectively develop the application. Framework coupling causes more issues than that, though a comprehensive overview of those is out of scope for this post.
An OOP Solution
Favor composition over inheritance -- OOP principle
Using an application as a framework is very similar to using inheritance for code reuse.
Just like with the application that is build on top of the app that acts as framework, the subclass might not be in control and be invoked many times from the base class. This is especially the case when using the Template Method Pattern and when having a deep inheritance hierarchy. The flow of control can bounce all over the place and decoupling the subclass from the classes up the hierarchy becomes all but impossible.
You can avoid this classical inheritance mess by using composition. Which suggests one way to move away from using an application as a framework or avoid doing so altogether: stop treating the framework as a base class. If there is code to share, use composition. This way you stay in control, can decouple easier and avoid The Fallacy of DRY.
Just like with class hierarchies you can always slap on an extra level.
Thanks to Raz Shuty for proofreading and making some suggestions.