Web Frameworks

Web apps are a class of application with sterotypical problems. Many problems with code complexity come with mixing up the web framework objects with business logic and service code. One important sign this happens is passing context dependent variables that represent constructs of the web framework to subroutines. There are two kinds of context dependent variables typical of web frameworks:

  • Request and Response objects

  • ORM objects if the web framework has or supports a database abstraction framework

You want to minimise passing of these objects through the call graph of your own code.

The principle feature of a web framework is the view function. The only relationship you want your view function to have with other parts of your code is data. The view function passes data to other parts of your application and gets data back. Passing a user id (int) is better than passing a User object. But if the called code will immediately again use that to query the database for user information, you now have an unnecessary call to the database. But maybe you have the data already, so pass that (company id?), but not in the form of a context-dependent variable that may have side effects and couples lower layers to the web framework. There are various options here but most importantly, you want to divest your function calls of couplings to your ORM or web framework.

If you end up passing the Request or ORM objects throughout your own code, far downstream, it will have dire consequences for readability and maintaining the code base.

If your view function can call the database, apply some business logic, return results, you are good to go. Don’t try to make the view function into an abstraction layer that hides everything else. If the functionality is too complex and would cause flake8 to judge the functionality of the view function to be over the accepted threshold, maybe reduce the view function to be short, handling mostly view things and let subroutines do the complex things. But do not then start coupling your view with the underlying routines by passing context dependent variables representing framework features. The benchmark is that the subroutines should be callable by non-view code. They should be easy to setup, easy to load in ipdb and ipython and require as few imports as possible.

If you are tempted to implement abstractions that support the Dependency Inversion Principle (DIP), be mindful that this can be hard to do when many web frameworks ensure tight coupling between high-level modules (your domain entitites and rules) and low level modules like the persistence layer. Fighting against this to achieve a Clean Architecure can have a high cost.