I'm trying to migrate to ember-cli from some old homegrown build tools. Our app is quite large and is actually split into several ember.js single page apps (e.g. index, admin, reports, etc) that share a common set of utils and components.
I'm trying to figure out if that's even possible with ember-cli and if so, how do I do it? I saw some people talking about pods, others talking about addons and yet another set of people talking about private bower repos. I tried finding out information on each of these, but it seems it's all in a bit of flux.
I'm not picky about the directory structure or the details. But I guess this is how I would envision it:
[app]
- [controllers]
- [models]
- [routes]
- [views]
- index.html
[admin]
- [controllers]
- [models]
- [routes]
- [views]
- index.html
[reports]
- [controllers]
- [models]
- [routes]
- [views]
- index.html
[shared_code]
- [components]
- [utils]
Brocfile.js
etc
Any advice would be greatly appreciated. Even just a starting point would be immensely helpful.
Edit (Jan 28th 2015):
Ember-cli addons are more stable now and could be used for this application. But IMHO they still have some short comings for this use case. They create more boiler plate as you still have to import individual models/controllers/components/etc into your application space. See the "Components" section under the addons here: http://www.ember-cli.com/#managing-addon-dependencies
There is also an interesting RFC to bring engine like support to ember and ember-cli that could satisfy this as well: https://github.com/emberjs/rfcs/pull/10
Edit (October 3rd 2015):
There is a new update to the Engines RFC and that looks promising for many users. However, we still have the need for multiple apps which are actually different. Another developer I work with spent sometime to flush out the details of how best to use this pattern.
I've documented that and created demo in a repo: https://github.com/workmanw/ember-multi-app
Ember-cli doesn’t support multiple apps out of the box. (By the way, I am still amazed how many things that were common in SproutCore are still problematic with Ember). The pods you mentioned are supported by tools which ember-cli depends on, so most of the ember-cli commands will work fine. The way resolver (an ember-cli dependency) puts everything together is described in its pull request. But you won’t be able to use generators because they are not aware of pods yet. Ember addons mostly extend ember-cli or Ember itself, although they may be able to solve your problem, they are not the right tool.
I think the best thing for you is to wait for more pods aware ember-cli commands or to implement this feature to ember-cli yourself.
The next best thing is to actually split your project into multiple projects, one per app, and include shared code via Bower, NPM or other solution. They all usually allow to import dependencies via git or file system if you have your own private components. You might have a super project where everything goes together (via NPM or Git submodules) and where you’d still have some homegrown solution to orchestrate everything (by basically delegating it to ember-cli).
If you must accomplish multiple apps with Ember CLI, per your 2015/1/28 edit, you'll want to wait for more pod support, or engines.
Consider however the naive DIY solution here: make your
n
separate Ember apps inton
separate Ember CLI apps. Serve them yourself in a single superproject.Disadvantages
As with your investigation into addons, you will have repeated boilerplate,
package.json
,environment.js
, etc. You will incur the overhead of maintaining each app's Ember, Ember CLI, etc. versions separately. You will have to create a way yourself to serve them all in a superproject. Even if multiple apps are on the same library version, clients will likely have to download duplicate vendor.js code.These are strong disadvantages you'll have to weigh.
Advantages
You must be disciplined about the code you extract for sharing.
"Duplication is far cheaper than the wrong abstraction." In the world of dynamic JavaScript, where anything goes in the type system, and everybody has a different module implementation, abstracting/extracting too early gets you into trouble. When shared code is carefully extracted into a separate library—perhaps using the Rule of 3—it results in a higher quality microlib. The lib's maintenance can be focused on optimizing just the shared functionality, and its backwards-compatibility. You might not get that if the shared code remains in your app, employed via import spaghetti.
As with frzng's answer, "include shared code via Bower, NPM," and Ember addons.
The tech debt of one app doesn't corrupt another app.
This is good particularly in the Ember ecosystem. Even though its motto is "stability without stagnation," new Ember patterns come out every day. Want to try one of those bleeding edge Ember techniques, but don't want to spend time upgrading your entire Ember monolith? Upgrade just one smaller app instead!
I've worked with a massive Ember app whose tech debt in small areas prohibited upgrading the entire app. With this scheme, don't need to say no anymore.
(Halting the spread of tech debt is part of why microservices are so popular lately. Many small Ember apps could be called a microservices approach.)
The Superproject
Organizing
You can explore Git
submodules (*shudder*)subtrees or NPM artifacts. You'll jump through hoops to keep them all in sync.In my situation, it doesn't make sense to run 1 app without the others. I've had success with a monorepo.
Serving
My URL scheme worked for the Unix philosophy-like separation of my Ember apps. Each could be served by a single server, but each grouped under a logically separate context path. For example, all requests to
/app1/*
get routed to app #1's compiled index.html. All requests to/app2/*
get routed to app #2's compiled index.html. And so on. Ember takes over routing from there.You might be able to do the same with
/index/*
,/admin/*
, and/reports/*
.To mount these apps to their various public URLs, add some metadata in each's
package.json
to tell the server how. On startup, the server loops through the metadatas, and dynamically generates web framework routes.