Ember engines introduces a powerful way to organize your application concerns, however it can be a little confusing if you aren’t familiar with its core concepts. Using an example of an imaginary little town I will try to cover the basic concepts in how engines, addons and shared dependencies relate to each other in ember.
Your app is a town.
Imagine your app is like a little town, something like Richard Scarry’s Busytown. Your town is full of shops and businesses, its streets are crowded with people and vehicles. There is only one town that all the shops are in, there may be more beyond Busytown’s horizon but we’ll never know because the busy townsfolk never have any time to check it out.
This little town is your application or host app in ember engine terms.
Your engines are the shops
The Grocery store, the bank, the VCR repair shop (that one is deprecated) all handle different focused concerns in the town. They share space within Busytown but they don’t need to be concerned with all the roads, other shops or people milling about. Each shop is just worried about their own business.
Each shop is an engine within your larger ember application.
Each shop is isolated from the others
The fish market focuses on fish market things (like the catch of the day) and the bank focuses on bank things (like loans for personal aircraft). Each shop also doesn’t care about the roads connecting their shops, their vehicles or traffic conditions.
This enforced isolation between the stores and the town itself is a separation of concerns that prevents entangling their concerns. This is what ember engines does as well, keeping individual engines isolated from each other and the host app itself.
Shops have customers
The shops do things the people of Busytown need. If a shop was just a closed box with no way in or out it would certainly not be a functioning business. Customers and stores need a consistent reliable interface in order to interact effectively. There needs to be a door to enter and exit the store and customers need to be able to interact with the store to buy goods and services.
Customers are like data in our application, and the interface between application data and our engine are defined using service dependencies. Each engine will declare its dependent services to the host app, and the host app will provide them to the engine via a strict interface.
Many clocks, one time.
Every shop has its own business hours, but they all synchronize with the town clock to know when its time to open and close up for the day. That way nobody is confused about what time it is. Imagine a business that didn’t match the town clock, it would cause a lot of confusion for customers who show up to find the shop closed!
This sort of global consistency is something that dependencies in engines are great for, because its a single source of truth shared across multiple engines in an application.
Shops can work together
Not all shops have the same customer needs. For instance the newspaper stand does not need a parking lot for vehicles, while for the gas station it’s critical to business. Furthermore a shop might need to trade goods with other shops to facilitate business. For example the cheese shop may want to transport cheese to the grocery store for sale. This requires a way for two stores to share cheese between them.
In ember we would call such a thing a shared addon. The addon would be declared in each engine and the host app, and will provide the same service to both engines. This gives engines a dedicated way to share data with each other.
The shops are in chaos
However, what if a shop can’t use the exact same interactions as other shops? Imagine that some stores start allowing customers to make purchases with credit cards, but the coffee shop can only handle cash? This case requires extra consideration, clearly it would be better if the all the stores in Busytown could transact money the same way for customers.
Sometimes there are good reasons shops are slow to update. There could simply be more important business priorities that have to be addressed first before upgrading. Its also possible that the upgrade is an experiment for some shops, but others are skeptical about adopting (crypto-currency). Maybe the butcher relies on an odd bug in the way it interacted with customers to supply its goods that is fixed in the latest version.
Changes in shared addons like these can be very important to control, and this isn’t handled by default for you with ember engines. However, it is possible to control via shared addon namespacing. Namespacing allows each engine to express its need for a specific version of a shared addon. The version can then be locked to the dependency declared in the engine’s
package.json itself, using a namespace.
Building a happy town
When working with engines it can be easy to get confused about how different parts relate to each other. I hope this post helps you feel more comfortable with this powerful tool and you are able to build your own happy little town.