Solving the integration puzzle in migration to Ember

This post follows up on our original blog on a progressive migration to Ember for enterprise SaaS products. 

In the previous post, we talked about the challenges we had faced in working with legacy architecture and how we overcame it. Also, we discussed integrating the Ember framework inside the RAILS application using Thoughtbot’s EmberCLI-Rails gem.

In this article, we would like to highlight some challenges on running the Ember app inside a jQuery based RAILS application. Major challenges were:

  • Conflicts due to incompatibility between older versions of JQuery and the prototype js in the Ember app
  • Achieving the same styling for components inside legacy and Ember app with RTL support
  • Development workflow
  • SPA experience during transition b/w Ember to the legacy module and vice versa
  • Building the first Ember module
  • Deployment changes
  • Continuous Ember version upgrades
  • Getting the team ready with the new framework & tools

Conflicts due to jQuery in Ember

In our legacy app, we used an older version of jQuery. Whereas Ember was dependent on a newer version of jQuery, which has overridden the older version of jQuery in the legacy code. So it might cause a problem with deprecated methods used in the legacy code. So, we decided to remove  jQuery inside the Ember app, due to the huge impact and testing effort in the existing project.

Ember also came up with an optional jQuery integration feature in Ember 3.4 release. It helped us to avoid using jQuery inside the Ember app.

We can configure in optional-features.json

We waited for the Froala editorversion 3.0.0 (WYSIWYG HTML Text Editor) to move away from jQuery for this purpose. Luckily, it was also taking place at that time. It helped us to cut the bundle size as well.

Conflicts due to prototype-js in Ember

By default, the Ember will extend the prototype of native objects. So, we disabled the EXTEND_PROTOTYPES configuration in the environment.js of the Ember app. When the Ember app was running inside the RAILS app after resolving the conflicts, we still faced some issues around the methods that were added in native objects and moment locales. We modified those implementations inside the legacy app, and it led to the resolution of the issues. 

Configuration in the environment.js

At this point, we had successfully integrated the Ember page inside the RAILS app.

Achieving styling consistency

We did a minor styling revamp last year during which we extracted all the common styles needed for both Ember and legacy apps. This common module  contains all layout level CSS variables, mixins and styles for common components like buttons, checkbox, modal, tooltips, forms, etc. Since these common styles won’t get frequently changed, we decided to maintain those files in both the legacy and in the Ember application to avoid last-minute surprises while we are completely moving to the Ember app. 

Though we maintain it in two places, we will exclude the unused files of the Ember application for a production build and the styles will be referred from the parent legacy application in the Production usage. But still, we are using those excluded files for our development purpose while running Ember as a stand-alone application.

Configuration in ember-cli-build.js

We have some legacy RTL mixins or compass-based mixins that might need minimal rework/removal when moving to a complete Ember app.

Development workflow challenges

Since we are using the hybrid approach, we are maintaining two workflows. One is RAILS with Ember (serves in 3000) and the other one is a stand-alone Ember app (serves in 4200) and this will be continuing until we migrate all the modules to Ember.

Even though we can access the Ember modules in the RAILS workflow, we recommend using a stand-alone application for development to avoid last-minute surprises and UI glitches while we are planning to move the Ember app as stand-alone in production. 

So, we made some configuration changes in the ember environment.js to achieve this workflow.

RAILS workflow configuration in environment.js

Ember workflow configuration in environment.js

With respect to this configuration, we will be rendering the ember index.html or index-dev.html. This rendering will be structured while bundling itself.

SPA experience during page transition b/w Ember to the legacy module and vice versa

After integrating the Ember application in Rails workflow, all the ember modules boot every time for every ember page hit, and it is not expected in SPA. In the legacy application, the SPA experience is achieved using the PJAX library.

With the first hit of the Ember module(page reload for the first time or a PJAX request), we fetch/boot the Ember application only during this time. The rest of the route transitions are handled without page refresh. This is achieved by exposing the Ember route service to the global namespace.

Refer to the flow diagram in Part I of this series.

app/instance-initializers/app.js

Below are the various transitions in this hybrid architecture:

  1. Global route service takes care of transition from Legacy to Ember route.
  2. Pjax takes care of the transition from Ember to Legacy route.
  3. Ember by default takes care of transitions within.

With this approach, we avoided both page refresh and redundant fetching of ember assets, thereby providing a good user experience.

Building the first Ember module

For our first migration, we decided to choose a simple module independent of  our core modules. So, we went for ‘Software’ under the Assets tab for migration. Before getting our hands dirty on code, we solved the below challenges: 

API Support: In Ember, we are using the active-model-adapter for seamlessly maintaining the response structure with the backend. So the API payload needs to follow the Ember adapter structure, and we brainstormed about the maintenance of relationships and sideload resources for various API scenarios. 

Maintaining the boot API calls: We required some common settings-related data before the ember modules got initialized. So, we exposed those data globally when the  RAILS started rendering the Ember itself. Also, we have implemented this only for the RAILS workflow to reduce the API calls for Ember initialization. Parallely, we have reused the same data via APIs for Ember workflow.  This we have achieved only in the RAILS workflow due to reducing the API calls while Ember initialization.

Ember Common components: While starting the base setup for the app code itself, we thought of building the common components for form, model, table, tooltip, dropdown, etc. Some components were built in-house and we used the Ember add-ons for the rest.

RTL: We use the ember-cli-rtlcss add-on to provide RTL support for the Ember modules. This automatically created the RTL counterpart for us.

Translations: Since we are migrating the existing modules, we have parsed all the related translation keys from YAML to JSON files for all the languages.

This is how we have rewritten the first module in Ember completely. Apart from that, we had a few more challenges on deployment changes, continuous upgrades of Ember and getting the team ready with the framework and tools, about which we will talk about in the next blog.

(This post was co-authored by Dinesh Kumar and Vigneshwar Krishnamurthy )

Cover design: Vignesh Rajan