MVC to Microservices From a Student’s Perspective

Jason Gerard
SSENSE-TECH
Published in
10 min readSep 11, 2020
Image by Gordon Johnson from Pixabay

What is the Microservice Architecture?

We have probably all heard of microservices before, but what does that really mean? Microservice architecture is a type of distributed system. This means we have our components decoupled or detached from each other, instead of having them all in the same place like with the Model-View-Controller (MVC) architecture pattern.

This allows us to break up our application, similar to how on a low-level we break up our classes and interfaces into different files. The general idea with this, is that most of our services are backend, which means they are simply dealing with business logic. Most of these services will take in data, do some operation, then return data back. These backend services send the data back to other services, including the frontend (FE) or user interface (UI). They talk to one another using various protocols such as HTTP and some kind of data-interchange format, which is just a fancy way of saying a way to easily send and receive data, a popular example of this being JSON.

The FE of our application is a service as well. It is the entry point into the rest of our code for the user. The FE, which might be a Javascript framework like Vue or React, will generally have a UI backend ‘attached’. This UI backend will act as a dummy or glue API between what the user interacts with on the website or app, and all the backend services. This means that the backend attached to the website or app has a single job, which is to format outgoing and incoming requests so that the actual UI doesn’t need to manipulate any data.

Every service is entirely self-contained. This means that they each have their own database if needed, and they only talk to other services through API calls. There are various reasons for using microservice architecture — which will be discussed later in the article — but there are limitations and downsides to consider as well.

Figure 1. MVC architecture diagram
Figure 2. Microservice architecture diagram

Mapping an MVC system to a microservice system

The key to understanding microservices is understanding how they came to exist in the first place. Like most things, they evolved out of necessity. Other architectures were not scaling effectively, so a new type of system had to be designed. More importantly, the design of microservice architecture is just an iteration on past architectures, not a complete redesign, so we can see how pieces of other architectures were used to create the standard microservice architecture.

The main idea is that we are taking each controller and model pair — such asCheckoutController and CheckoutModel — from our MVC project and converting it into an entirely new service, most likely named CheckoutService. The CheckoutService is now its own entire web server with its own database, all of which is completely independent and decoupled from any other services.

The views then get migrated into their own service. This is generally a single monolithic service, but it could also be a micro frontend, although this is out of the scope of this article. The frontend code, for example, a Javascript FE framework, will still need some backend code in order to communicate with all the other services. For this, we will have a UI backend, which will act as a backend for the front-end (BFF). Its sole purpose is to bridge the FE and the other microservices. This is mainly done by normalizing the data of incoming and outgoing requests, allowing for slight variations between how different frontends handle and work with the microservices, so the FE only has to display the UI and nothing more.

The beauty of this type of FE system is that we aren’t solely reliant on a browser or a mobile app like MVC is. We can add as many frontends as we want, from a website on a browser, as shown in the MVC diagram above, to an app on an Apple TV, and still use the same backend services.

Differences between microservices and MVC / other architectures

The first thing most of us are probably taught in our first enterprise web applications course is the MVC architecture. On smaller applications, like what is worked on in school, MVC is great. It is fast to develop, has very little boilerplate code compared to microservices, and is easily manageable since all the files are close together. We have everything from our UI to our database interactions all in a singular src folder.

The first problem we will encounter is when we need to scale up our app. This could mean logistically, by adding more software engineers to the project, vertically scaling our application by increasing the resource allocation of the hardware it is running on, or by making our app bigger with more features. Growing an MVC app from 2,000 lines of code to 10,000 lines of code would most likely cause the software engineers to spend half their days fixing merge conflicts and resolving issues when they inevitably break other services. This is primarily a byproduct of tightly coupled services.

With microservices, the software engineers might encounter this problem in the form of two services. For example, a checkout service and a cart service might currently be a single microservice tightly coupled together. As the project expands, we might find that we want to decouple the two, that is, split them into two distinct microservices and have no overlap between them besides API communication. This might be needed for various reasons, but let’s say it is because we want to be able to checkout and complete a transaction, without the use of a cart. Creating a new microservice is quite similar to how many Object-Oriented Programming (OOP) languages overcome the same issue between classes, which is by using encapsulation and data hiding. We can make parts of our checkout service ‘private’ and inaccessible to other services (i.e. our new cart service). This means anything in our cart service that isn’t accessible through an API call, would be ‘private’, and inversely any function or data accessible through an API call would be ‘public’. Similar to how getters and setters would be used as a strict public way to access and set private data. This means we can enforce data hiding and service level encapsulation of our classes, interfaces, functions, and even database access.

In MVC, we would be hard-pressed to do this. There are ways with proper folder structure and organization, but, in the real world, this is rarely the case. Most projects grow to be much larger than initially expected, and having a strict folder setup in order to force inaccessible code is in itself not scalable or maintainable. There is also the logistic aspect of having software engineers not use parts of the code without strict enforcement.

In an MVC application, your controller will be sending the rendered view or FE to the client. This means that the application will dictate what views the client receives, this might not seem important but it has many implications. In other words, the server is deciding for the user what to display. This might be worded a little oddly, but think about it like this: if the user wants to use the checkout service on a mobile app instead of a web app, this translates to the user wanting to decide which views/UI they want to use to interact with the checkout service.

MVC applications don’t allow the user to make that decision. They already have a set of predetermined views or a UI that the user must use in order to interact with the service. This means that if we want to add a mobile app, we need to rewrite or copy and paste most, if not all, of our backend code. This will yield the same results, in addition to serving the user a different UI. So, as our app gets bigger, the general way they scale up is as depicted in Figure 3.

Figure 3. MVC to microservice transition

Another difference we see with microservice architecture versus an MVC or monolithic application, is that the code of most services is inherently abstracted, which could cause a lack of visibility between teams. Some teams might have never seen the code behind APIs they call every day. They receive documentation on the endpoints they need to communicate with and the contracts on how the data should look like. While on the MVC side it might be as simple as just peeking into the method body in the same IDE window. This is not necessarily a pro or a con, just a difference. One could make the argument that it is better for software engineers of a particular team to know their code extremely well. Others might argue that while that is true, they only know their own code very well and lack any meaningful understanding of the code from other teams.

How and why we use microservices

The purpose of microservices is best explained using an example. Imagine we have an ecommerce site with the following features:

  • Accounts
  • Login
  • Cart
  • Checkout
  • Products
  • Inventory

These are all small sections of the site that work together to give the user the full experience, but they should be completely independent of each other. After all, why does the cart need to know how to login the user?

This gives us the first use case of microservices: to be able to easily decouple different services. It provides us with a lot of benefits such as easily re-usable services. If our ecommerce business grows and we want to build a mobile app, by using a microservice architecture we can re-use the same services to supply the needed data to our app’s UI. With decoupled services, we can now more easily implement unit and functional tests for our app. This, in turn, means more and better testing, which is likely to improve the quality of the final product. We can smoothly deploy certain components of the app and revert back to older versions if needed without impacting any other flow.

Another really nice thing about microservices is that they provide us with the ability to abstract away other services. As a distributed system grows, it can have infinitely many microservices. Let’s say our site has 200 microservices, with some bigger and some more important than others. When hiring a new software engineer for a specific team, we can abstract all the work of the other teams away and reduce it all to a simple API call, which is hopefully well-documented. This allows for faster onboarding and an easier understanding of the codebase.

Overall limitations and cons of microservices

The biggest or most apparent downside of the microservice architecture is the fact that it is so verbose. Everything you want to add that isn’t already encapsulated by another service must be abstracted into its own service. This means a lot of boilerplate code and configuration, as opposed to another architecture where you might just create a new service file and start working. This results in an increase in time for developing new features and a decrease in developer productivity.

Something that isn’t often addressed, is the paradox of choice. In this instance, this refers to software engineers making their application so extensible that a software engineer might have two or three equally good ways of implementing a new feature. They might choose option A. Another software engineer might go with option B. Both are equally valid, but now we have different ways of doing the same thing within the same code base.

Oftentimes, we see a much worse situation unfold where the feature gets gridlocked by decision making. In this scenario, every minor detail goes through extensive architecture review because no one is quite sure what the best way to design or implement the feature. As engineers, we must always strive to produce the best work possible, which is great, except when it starts to hurt productivity.

Conclusion

Overall, there are many pros and cons to implementing a distributed system. While microservice architecture is just one of many ways, it is the most popular and has become the industry-standard amongst large companies right now for good reasons. This type of architecture allows for enough extensibility and options that it can be made to adapt to most of the needs of a given system.

Hopefully, this article has provided a clear explanation of the transition from another architecture, such as MVC, to the microservice architecture. If you wish to explore microservices further, you can take a deeper look at some of these advanced topics:

  • How microservices can communicate with each other asynchronously with event busses, queues, event sourcing vs event driven, and publisher-subscriber pattern
  • The different types of frontends we can implement with microservices, such as micro frontends vs monoliths
  • The different types of architectural patterns we can use to implement a microservice at scale, such as domain-driven-design
  • As your project/application scales to microservice architectures, your databases will need to scale as well. Examine how to scale databases and optimize them with table sharding, functional partitioning, and master/replica patterns
  • How we can deploy, manage dependencies, and vertically/horizontally scale our microservices with tools such as Docker, Docker Compose, Docker Swarm, and Kubernetes

Editorial reviews by Deanna Chow, Liela Touré, & Pablo Martinez.

Want to work with us? Click here to see all open positions at SSENSE!

--

--