What is an Ember Engines?
According to Ember Engines documentation, an Engine represents a set of functionality and user experiences that could logically be considered an application. It’s a specific type of Ember addon that relies on its hosts to fulfill these responsibilities. From the user’s perspective, the host application and its Engines appear to be a single application.
It’s already being used by a number of organizations, including LinkedIn, CLARK, and Square to optimize sites with millions of users.
You can use Ember Engines in a variety of formats, depending on your needs. Here are some examples:
Standalone
Standard addon (normally just referred to as addons) is full-fledged NPM packages that can be distributed and installed in other Ember applications.
In-repo
This type of addon lives within the repository of an Ember application and is only used by that specific application. Using an in-repo Engine is a good alternative to consider instead of maintaining two separate repositories.
Route-less
They don’t live at a specific route but are instead mounted within a template or your application. Examples of good candidates for route-less Engines are chat boxes or complex forms.
Routable
They live at a designated route in your application and can have their own internal routes which you can navigate to.
After some deliberations, I’ve decided to use a routable in-repo Engine. Since our feature will be composed of multiple routes and its own business logic, I’ll isolate it from the parent app as a routable Engine. Additionally, I’d like to keep using the full CI/CD process because our frontend repo is rapidly changing in most areas; so the Engine will have to be in-repo since in this case, a standalone Engine might slow down the development process.
Working with Ember Engines was overall a good experience. The only roadblock I experienced during development was the need for pre-planning for how we would share components from the parent app. I ended up adding a shared add-on for all required components, data, and so on. It did require a bit more work but it’s a great investment for the future since to this day, our team adds any needed components or data when it’s in broader use.
Strengths Of Ember Engines
Performance
The coolest concept of the Engine’s source code is that it can be asynchronously loaded when the Engine is mounted. When set as true, only the Engine’s routing map is initially loaded. The rest of the Engine can be loaded when a route in an Engine is visited. This ability to optimize performance is crucial and eventually saves money.
Maintenance
Using Ember Engines reduces the complexity of a huge dependency tree that keeps growing as we go along. The fact that small chunks of the app are loosely coupled can eventually help us to avoid unwanted side effects and iterate quicker. A more forward-looking approach like this can also help newcomers feel less overwhelmed and get them integrated faster.
Weak spots Of Ember Engines
Testing
In-repo engine testing can be challenging since it is not fully decoupled. Meaning, you’ll have to keep all your tests in the parent app. Specifically, the issue with unit and integration tests is more complex because your parent app will not find the component you’re testing. We worked our way around it and Happily the testing resolver to support multiple engines using the module prefix. Full details can be found in our suggestion in #653 PR. Happily, Ember Engines Roadmap does include fixing this issue for 2022.
Dependencies
Since the Engine functions as a mini-app, it has its own dependencies. It makes sense for a standalone Engine but when dealing with in-repo, it’s up to you to manage and maintain different versions of packages across your Engines and parent app. The real issue begins when you have different versions of packages across your Engines and parent app. For example, imagine the parent app depends on ember-truth-helpers~1.0.0, Engine#1 depends on ember-truth-helpers~1.7.0 and Engine#2 depends on ember-truth-helpers~2.1.0.
Now, three different versions of ember-truth-helpers would be included in your vendor bundles. But which version would actually be used in your app/Engine would depend on the order in which the bundles are loaded.
*Using yarn workspaces can be a good solution to manage duplications of dependencies since it unifies all required packages in one process.
Sharing code
When you start to work on an in-repo Engine, in an existing app, you might find yourself needing existing components, helpers, and utilities. For the existing repo, you might be investing most of your time in pre-planning. To avoid incidental coupling with a specific host design and architecture, Ember encourages moving the shared constructs into an addon. It is easy to understand the reasoning behind this, since it supports the concept of having an independent Engine but eventually, it creates a longer pre-planning process and makes you choose between duplications (no one wants that) and extracting existing components into a shared addon. A mid-way solution would be to inject a component dependency as we do with services, for example.
Final thoughts
The concept of Ember Engines makes it possible to separate individual pieces of your application and it enables our different teams to iterate quickly and independently.
I recommend trying out using this great tool in your Ember app, but I strongly believe that for an existing large app, you should invest some time in planning ahead.
, Senior Staff Software Engineer at LinkedIn, explained it best in his tweet thread:I hope you enjoyed this post! Clap 👏 and share away!
WMK Tech Copyright © 2024. All rights reserved