Everything Old is New Again

Everything Old is New Again

I like to believe in the Polymath. A polymath is often seen as someone who is capable in multiple disciplines, and particularly adept at applying patterns from one discipline or industry in others. I also believe Polymaths need to be historians, able to understand and adapt past practices to modern challenges.

In many ways, our industry internalizes this concept – continuous learning and iteration fuel innovation. Often, it is small improvements to the hard-won ideas of many who came before vs. something completely new that launches the next paradigm shift. DevOps and SRE are natural evolutions of Infrastructure Engineering. Micro-services and API-first architectures can be seen as progressive SOA. Much like astronomy or medicine, we don't simply rest on the shoulders of giants but use their height as a springboard to what comes next.

Learning from the past and continuously improving is noble, but that doesn't mean old patterns fail to be useful. This is why historic awareness (not just keeping up with trends) is important to the modern polymath.

MVC

MVC is a software design pattern which suggests program logic be divided into three key components:

Model – Data structures and management of what is being manipulated (users, widgets, space ships).

View – Presentation layer. Multiple views of a single model are common (YAML vs JSON).

Controller – Accepts input, often responsible for sanitation or transformation.

The pattern is nothing new, dating back to the 1970's and the Smalltalk ecosystem where many OOP ideas were born. Like any pattern, MVC represents the core of a solution which can be adapted as needed (e.g. UI vs API architecture). As a result, MVC has given birth to subsequent patterns such as HMVC, MVA, MVP and MVVM. Each iteration attempts to adapt MVC to a specific context. The sheer number of variations can be seen as evidence of MVC's benefits – imitation is the sincerest form of flattery.

While I am sure to omit some detail, MVC has several key goals. By logically grouping major application components, it encourages separation of concerns (a'la the Single Responsibility Principle). Aside from adherence to SOLID principles, this leads to natural decoupling. While loose coupling is all the rage in a cloud native world, it has long been important to encourage reuse and facilitate parallel development. By separating controller and view logic, backend and frontend teams can iterate faster and evolve independently by using interface mocks or other tricks of the trade to simulate the model which binds them together. You also benefit from high cohesion, since code relating to specific M/V/C activities tends to be naturally organized together.

Architectural design decisions are like most decisions in life – rarely black and white, but more about the tradeoffs which you feel best fit your context. Common pitfalls of adopting the MVC pattern include increased complexity (messier code navigation, steeper learning curve for new developers) and lack of consistency due to the natural scattering of implementation details across different components. Frameworks or boilerplate can help with the learning curve, but abused boilerplate is another gotcha... often manifested as clustering too much logic within a single component while others serve only as shims.

MVC Boilerplate for Go

While we must be mindful of potential pitfalls, boilerplate can help lessen the learning curve by allowing teams to focus on core functionality rather than directory structures or other mundane tasks. This is why create-react-app and similar tools gained such popularity.

Many languages have MVC frameworks including Django for Python and Rails for Ruby (both server-side). The Javascript world has numerous projects including Angular and Ember.js which take a hybrid approach (pushing more logic to the client). The diversity in approach further exemplifies the adaptability of MVC!

When learning about MVC in the context of developing micro-services in Go, I found myself wanting an opinionated framework to save time. Seasoned Go programmers often abhor frameworks ("just do it with stdlib" is often shouted on subreddits), but balance is key. That's why larger projects often minimally pull in something like Gorilla or Gin! Reinventing the wheel is typically a waste of time (one might say you could just use core Node.js functionality to implement modern web-apps, and in fact you could, but frameworks like Fastify are popular for good reasons). I realized "framework" had too many negative connotations in the Go world, and "boilerplate" or "starter project" was really what I was after... little more than a directory structure and some loosely-held opinions I could quickly clone and adapt as needed. Something more akin to create-react-app than Django or Express.

To that end, I've put together a small starter project you can use when developing API-first micro-services in Go. It is just that, a starting point... drop parts you don't need and add what's missing! It provides a simple directory structure encapsulating key elements of the MVC pattern, as well as a few opinions on how to organize a modern software project. Some of these, like breaking out the "app" code, come from experience with Node.js and might not be to your taste. Others (separate service and util directories) are meant to lend organization to larger projects, but might not make sense in a tiny micro-service. Some bits are completely missing (where do you put your tests?), leaving room for interpretation – and that's the point. Please check it out, clone and adapt as needed, and feel free to share or submit improvement ideas if you find it useful!

Mike Hoskins / go-mvc-boilerplate
🚀 Starter project to help kickstart your MVC-based Go API 🚀