Thin Server the Hard Way (Basic Architecture)
As I mentioned in my previous post, I’m looking to create a basic Thin Server Architecture without any particular framework, mostly to see what the pain points are. Knowing what problems these MV* frameworks are trying to solve is going to be critical in further evaluation.
So, with that as the goal, I created a simple Thin Server front end around the REST endpoints provided by QEDServer.
I’ve set up a Github repository to hold all the Thin Server clients I’m writing, and made this project a subdirectory in the repo. To use this subdirectory, download QEDServer, clone the Github repo, and symlink QEDServer public directory to point to the subdirectory. I do recommend you try this out if you’re going to read further, since it’ll show clearly what kinds of nifty responsive layout options you get from Bootstrap, if nothing else.
As a side note before we go any further, I do feel the need to mention that I’m not particularly proud of this code – it served it’s purpose, and it’s mostly bug free, but as I continued on, I started a number of refactorings which I never quite finished. For that reason, I almost didn’t release it – but in the end, I decided it was worthwhile as a launch point for a discussion.
So, on the to code.
This section contains the Navigation and branding that’s used throughout the application. The markup is pretty simple, and really shows off what kinds of things that Bootstrap can do. That’s the topic for a whole separate post.
Underneath the navigation section are all the different “pages” which will be visible to our users. Since only one will be visible at any given time, I opted to place all of them in separate divs, and then switch between them via hide/show. This is a very performant way to do things, but has two drawbacks – you take a hit on initial load, and for large apps, it will become utterly unwieldy.
Similarly, I have a separate cloning area for sections of HTML code which I’ll be copying and placing into the app, completely wrapped in a hidden div. Again, this is a simple, fairly performant way to do things, at a cost in initial load and maintainability.
At almost 800 lines, this ended up being a pretty big lump of code. I opted to use the module pattern (with import and export) to organize things. I further divided up the code into several modules, to do a proper division of responsibilities – and since it was going to be evaluating MV* frameworks, it seemed to make sense to use a similar structure.
The first module just controls the view layer (in conjunction with the HTML, meaning that that doesn’t fit properly into an MVC pattern). A variety of functions handle displaying the different pages via show/hide on the divs in the page, as well as event handlers, an alert messaging system, and other action functions for adding and deleting products. I refactored this code a number of times (and actually stopped in the middle of my last refactoring), but never really got the kind of cleanliness that I wanted. In particular, I really wanted to separate out the binding between the rendered HTML and the functions that acted on it that I felt would be necessary for maintainable code. Of the three layers I tackled, this one was the one that left me the most dissatisfied.
Front Controller / Router
By using onhashchange as a listener for the routing code, it was pretty easy to come up with a basic routing mechanism. With the exception of some annoying boilerplate that I had to write for parameter handling, this proved to be some of the easiest code to write for the whole app. While complexity would grow linearly with the complexity of the app, it doesn’t look like this is really something that other MV* frameworks are going to address – but maybe they’ll surprise me. It would be nice to have something that handled
history.pushState automatically, but since QEDServer doesn’t really handle that, I didn’t take a stab at it.
A thin ajax layer across the REST interface of QEDServer was, like the router code, rather easy to write (once jQuery’s excellent ajax support was added in). Since it’s only a thin layer, it does expose the underlying data structure of the REST server to the View layer, but you could just as easily change it to massage the returned data to any desired format, in the event that the returned data changed it’s structure. While there was a tiny bit of boilerplate, this doesn’t seem to be that big a deal. Adding in CORS support looks easy, but I didn’t try it, since QEDServer didn’t support it. Like the router code, this looks like complexity will grow linearly with the complexity of the REST API being modeled, and it’s not clear how a framework could really improve on things. Again, I’m hoping somebody will surprise and delight me by proving me wrong.
Initialization and utility methods
The end blocks contain a variety of utility and initialization methods. There was a fair bit of code in here that really felt like reinventing the wheel, and in the end, I even wrote a really primitive templating solution – I’ve no doubt that this is tackled in any number of other places (and more recent efforts have certainly proved that out).
I was more than a little surprised by what I found. The two bits I thought would be hard, routing and Ajax retrieval from REST, were handled pretty easily, while the part I thought I knew how to do, view manipulation, proved to be a pretty tough nut to crack.
- Templating is a pretty large need.
- Balancing initial load time with performant changes
- Better code organization for large applications
- A simple setup for routing would be convenient, but not especially critical
- Removing some boilerplate for REST endpoint wrapping would be a nice-to-have
- View state management looks to be critical.
- Binding both data values and entire views to HTML tags
- Binding event handlers
- Managing view transitions and lifecycle
I’ve already ported this application to the first MV* framework that I looked it, Backbone, but it’s probably worthwhile going through a few more items on this first effort first before diving in. Look for that next.