Where to put the Web in Software

Last week, I wrote about routing in web frameworks, specifically arguing for decentralized routing over centralized routing. Through writing that blog post, I stumbled upon a more general point, which I will try to explore here.

The spark for this discussion comes from Robert C Martin’s concept of being Framework Bound. Martin says that you are framework bound if you cannot easily change your framework; you are framework bound if your business logic is mixed in with your web logic. This makes a statement about where the web belongs: as a thin, exchangeable layer outside of your business logic.

Image: Monolithic web, with business separated from the web layer.

I am going to take this a step further, and in doing so, I will assume that you write code that does not leave you framework bound,[1] or at least that you understand the concept.

The Web in Software With Simple Components #

A good software design is cohesive and has clean interfaces. It is easy to get an overview of the software at every level of abstraction, because the complexity has been pushed out, leaving each component simple and understandable.

That means that your actual software is not a big monolith, as pictured above, and assumed in the Martin post. It also means that defining an arbitrary “external interface” that all interaction must go through brings you further from reality, and adds unnecessary constraints to your software, which will inevitably be at odds with the principles of cohesion and pushing complexity.

Good software consists of small, tight modules with long dependency trees. This means that you have the option to use whichever module you want to use, at any level of abstraction (though some modules wrap around domains more nicely).

Image: This is how your software stack actually looks: small, interdependent modules.

What you see here is that your business logic’s interface with the world isn’t a single, unbroken line, drawn by a single module. It is, in fact, the surface area of a lot of little modules.

Realizing this will allow us to build a web front that fits our software. Instead of a monolithic web app, we should wrap the outermost (customer-facing) business logic in small web-oriented components.

Image: Making the outermost modules ready for the web, by wrapping them in a different module.

Because we have made web-components in the same way that we make software we can now build our final app by simply composing the web-oriented modules.[2]

One component could be an authentication module, that can be reused across web apps authenticate in a similar way. Another component could handle blog posts. A third could handle static pages. The last two could share a lot of web-oriented code (editor, rendering Markdown), but be entirely separate components.

Each module is entirely self-contained. They can be configured to use any underlying data and communications structure. This makes it easy to spin up an entirely separate instance of your app (with a different data layer), or an app with limited capabilities (by composing a subset of the web-components).

The end result is a structure that looks like this:

Image: Composing components to build a web app.

The web is wrapped around each business component, composed to make apps.


Notes #

  1. Writing code that does not leave you framework bound is harder in some frameworks than it is in others. Often, a good indicator that a framework might bind you, is that it forces its own structure on you (folder structure, class hierachy, etc.)
  2. I gave an example of building web apps by composing sub-apps in Centralized vs. Decentralized Routing.
 
3
Kudos
 
3
Kudos

Now read this

if/else -> if-return

You want to write readable code. Readable code has short functions and is easy to get an overview of. Being able to follow a single path of execution until the end before starting the next is easier than having multiple possible paths in... Continue →