Decomposing Monolithic View Systems

Development

Reading Time: 6 minutes

When we break down a monolithic app into microservices, we go through a process known as decomposition. Simply put: This process can be a bit overwhelming. After all, if you have a really large monolith, you have a ton of code to organize and decompose! Because of this, I believe decomposition is best implemented as a slow and gradual process.

Before we dive into implementation details, we want to figure out what to decompose and what we want to decompose it into.

It’s important to organize each piece of decomposition into areas that are organized around functionality. Some of these groupings can be a bit difficult to figure out. Many apps don’t have well-defined sections. However, a simple place to start is at the view layer.

In a normal MVC-based monolith, the model, view, and controller logic are tightly coupled. With decomposition, we want to extract the view layer into a separate application, while leaving the rest of the application’s existing logic in its place.

To accomplish all of this effectively, we’re going to have to change up the way we pace ourselves writing software.

Breaking Your Development Rhythm

Each team has its own unique cadence by which it makes improvements to software. If we were to guess the process for creating new view layer logic in a monolith, it might look something like this:

  1. Update model and controller logic;
  2. Create view logic;
  3. Profit.

When we start leveraging the separate view service, our process will start to look something like this:

  1. Update model and controller logic – MVC backend;
  2. Serialize resources to be passed into View Service – MVC backend;
  3. Update model and resources to receive backend data – View service;
  4. Create view logic – View service;
  5. Profit.

As you can see, we added a few more steps to our process. It might seem like more of a burden, but the resulting product should help you better distribute the workload throughout your application. We lose the simplicity of keeping the view layer on the server side, but we gain the advantage of delegating certain tasks and functions to the client side!

Let’s take a look at a more technical implementation of this.

Sign up for a free Codeship Account

Pulling Apart Our Server-Side Views

The example I want to walk through involves decomposing Rails’ view layer into an EmberJS view service. To do this, I’ll be leveraging a tool called Ember Islands. Ember Islands is a popular Ember add-on that allows us to render Ember components within server-side views.

We achieve this decomposition by leveraging Ember components. Ember components are reusable pieces of Ember views and logic that can be rendered anywhere in our Ember app. However, we also want to be able to render certain parts (e.g., Ember components) in server-side views outside the Ember App.

Ember components are mainly made up of two files:

  • A logic file (app/components/[component name].js)
  • A view file (app/views/components/[component name]/hbs)

In many ways, we’re adding another view layer to our MVC app. Eventually, we’ll have enough components built that switching into a full-blown Ember frontend will make sense. The process allows us to better ease into that transition instead of sprinting into it full-force.

While we’re not going to walk through an entire project’s worth of decomposition, I do want to focus in on an example of a Post resource Index. First let’s take a peek at our controller and view logic on the Rails side of things:

app/controllers/posts_controller.rb

class PostsController < ApplicationController

  # Grabbing 10 most recent posts
  def index
    @posts = Post.all.order('updated_at DESC').limit(10)
  end

end

app/views/posts/index.html.erb

<h1>Posts</h1>
<% @posts.each do |post| %>
  <div class="post">
    <h2 class="title"><%= post.title %></h2>
    <div class="body">
      <%= post.body %>
    </div>
  </div>
<% end %>

As you can see, we’re listing all our Post objects into their own <div>. However, all of this logic is still server side. Our goal is to extract some of this functionality into the Ember side of things.

Think of this view like a cake. If the cake quality is pretty good, how we cut it won’t effect its overall taste. However, how we section up this cake will determine how equally the workload is distributed across our entire service ecosystem.

Option 1:

index.html.erb

<h1>Posts</h1>
<% @posts.each do |post| %>
  <div data-component='post-preview' data-attrs='{"post": "<%= post.as_json %>" }'></div>
<% end %>

Option 1 is technically more reusable since we might be able to find a place to use the post-preview section somewhere else in our app. However, its scope might cause some issues. For example, what if I wanted to do some Ember-side filtering of most posts’ data? Since we’re only focused on a single Post object, we’re unable to effectively interact with the @posts collection at large.

Option 2:

index.html.erb

<h1>Posts</h1>
<div data-component='recent-posts' data-attrs='{"posts": "<%= @posts.as_json %>"}'></div>

Option 2 seems to make the most sense for the context of what we’re trying to accomplish. We want to be able to interact with a collection of Post data on the client side (instead of server side).

Now that we have a good idea of how we want to decompose our initial view, let’s write the logic to do so!

Building Out the Component

Now that we’ve ironed out how we want to extract our server-side view, let’s build out the Ember component.

Let’s start with the component logic.

app/components/recent-posts.js

import Ember from 'ember';
const { Component } = Ember;

export default Component.extend({
  posts: null,

  init() {
    this.set('posts', this.get('posts');
  }
});

In our init() function, we’re setting the client-side posts variable to the server-side Post data that we’re passing into our component. Otherwise, our component is fairly bare bones. We want to start small with decomposition.

Next, let’s write our component template:

app/templates/components/recent-posts.hbs

{{#if posts}}
    {{#each post in posts}}
        <div class="post">
          <h2 class="title">{{post.title}}</h2>
          <div class="body">
            {{post.body}}
          </div>
        </div>
    {{/each}}
{{else}}
    <h2>Unable to find any recent posts!</h2>
{{/if}}

This view iterates on our original view with a few twists. Because of our component design, we’re checking to see if we have been passed any relevant posts. Otherwise, we’re simply translating the .erb Rails logic into Ember logic.

With all of these elements combined, we will have successfully created a bridge between our server-side (Rails) and our client-side (Ember) applications! While the two services are still more coupled than we would like them to be, developing this way allows us to slowly take small but effective steps toward completely pulling apart the view layer from our MVC monolith.

Next Steps in Decomposing Monoliths

From our current state, we have a world of possibilities before us.

In order to start leveraging the data storage aspect of things, there’s a need to create models and routes in the Ember view service. In Ember terms, we can modify our component to look like this:

app/components/recent-posts.js

import Ember from 'ember';
const { Component, inject } = Ember;

export default Component.extend({
  store: inject.service(),
  posts: null,

  init() {
    this.set('posts', this.get('posts');
  }
});

Injecting the store service allows us to leverage Ember datastore functions from within our component (e.g., this.get('store').[DS Method]). With this functionality, we’ll be able to split database calls between the Rails and Ember sections of our application.

Once you begin to feel comfortable with configuring an initial section of decomposition for your application, try breaking down more and more pieces of your view layer. This might take a little bit of time and tinkering to get used to, but you’ll eventually find yourself becoming more and more effective at view decomposition.

Subscribe via Email

Over 60,000 people from companies like Netflix, Apple, Spotify and O'Reilly are reading our articles.
Subscribe to receive a weekly newsletter with articles around Continuous Integration, Docker, and software development best practices.



We promise that we won't spam you. You can unsubscribe any time.

Join the Discussion

Leave us some comments on what you think about this topic or if you like to add something.

  • zubair alam

    How you manage static assets in above scenario?

    • Taylor Jones

      Hi Zubair! Thanks for taking a second to respond!

      So, static assets in this scenario would be handled by the Rails backend. After all, we’re embedding Ember into certain views of our monolith. In terms of how to pass those images from the Rails side to the Ember side, that can depend. Personally, I tend to use storage services like Amazon S3 to store my images and then serve them via a url. That way, I can just pass a url between both services.

  • Casey Provost

    Not to be contrary but isn’t swapping ERB for JS simply just changing which technology in the view stack you are growing? In this most basic example we swapped out 1 erb file that was easy to maintain with 3 JS files. If we applied this pattern throughout the app wouldn’t we have a view layer that was 2-3x the size of the original?

    This particular approach of splitting out the client is fairly trendy today amongst startups in particular. While separating them out 100% to api and client does remove views by some large portion from the app I can’t help but question the gains when that almost directly means business logic has to be duplicated client side in order to achieve more complex renderings.

    Maybe you could provide some examples of application scenarios this works well in? I am currently part of a shop that moved a lot of views to React and even with seasoned JS people it is harder to maintain than straight ERB is due to all the tech you have to add to the stack for it.

    I would really love to see more “why” in this article and the pros and cons versus various approaches like Rails engines, presenters, etc.

    • Taylor Jones

      Hey Casey! Thanks for taking a second to write up some thoughts.

      There’s a lot to say about the pros and cons of going the route of switching up from Monolithic views (Rails ERB, for example) to Microservice Views (API passing data to Ember, Angular, React, etc). In the short term, you’re bulking up your views because you’re using .erb and rendering Ember stuff. The advantage is that you get to gradually build out your front-end components, but the con is that you’re adding a lot of bulk to your app.

      When that hard change to the View Microservice happens, you’ll lose some of this weight. However, in the best case, it should be no different than building a frontend app that depends on some random backend like Firebase. The whole point is to separate your concerns to best leverage the advantages of a front-end javascript framework.

      All of this being said, there are so many potential problems that arise from this. For your story, there’s a unforeseen need for shifting your team’s development priorities. In many ways, Javascript frameworks are a fad. However, there is a very strong value to using them. What that value actually is varies for how your team is using it. If they’re using it for the sake of using ___ language / framework. Maybe some soul searching is needed.

      I absolutely agree with the need for more of a why for this idea. My goal was to explain how we can decompose (practically) monolithic views. However, why we would want to do such a thing is worth of a couple of articles. Stay tuned.

    • Phillip Novess

      I agree it is very complex moving that direction. I’ve been learning react and it’s been a challenge. Completely different concepts, tooling, testing, etc. However, having worked on large projects that have had to have “just the design updated”, it has always been such a hurdle that almost all the devs on the team have cried themselves to sleep begging to re-write major parts of the application just to facilitate decoupling and achieving the goal.

      The first benefit I see, especially from the react paradigm of front end development, is the level of reusability. Thinking of the application as reusable components that have their own behavior, and state, allows developing that code in one place but those updates are applied across the entire application. It brings more consistency in the UI by building functionality that is the same across the entire application. This can be huge for a larger application. I rarely just build a table anymore for a search result. Product owners, clients, users want not just a list of results. They want complete behavior that include pagination, filters, links to the detail information, sorting on column names. Doing that across for the entire application one table at a time, versus in one place and then having any updates roll out everywhere, is huge. That being said, the extra tooling and management can be a deficit on smaller applications.

      Another benefit I notice as I switch back and forth between rails and react projects, is the package management. Gems for javascript assets is a very brittle solution. And sprockets is nice for basic things but it’s old and slower, and very rigid compared to all that webpack can do now. Every time I jump back into rails I end up wishing I could just type npm install for something and import it into my code.

      Also, I can see the benefits from a product management standpoint as well. Being able to take the core engine of your app, out of the UI allows you to branch your product offering into multipl products – API product for B2B customers to build custom solutions, mobile apps, desktop, etc.

      As far as the amount of code, it’s gonna get big. Its going to grow. It is a completely new project on top of the core project. But from my experience working on projects where we did our integration / feature testing using capybara, and still having broken front end code, I can tell you the testing is going to be better in the context of the front end instead of a backend trying to test the front end. Especially, as the front end becomes more intelligent and complex. Remember trying to find jquery bugs even though your rails feature tests pass? Sucks.

      On the flip side I do agree its complex and bulky. And the javascript landscape is in flux – pun intended. :) But it is gaining momentum around some specific standards. Certain tooling stacks are becoming more consistent and popular. Facebook helped accelerate this with the release of the create_react_app tool for react.

      Long term it makes a lot of sense. Yeah it’s tough for small teams to pivot to the new paradigm of development for web applications. But really, it’s a natural evolution. Web development has always been a client/server architecture. So getting better client technologies and having the options for more clients is an exciting thing.

      Just some of my experiences and opinions. It’s exciting times in the JS world. Reminds me of when I first started developing in rails. Have fun and good luck.