Services: A Rumination on Introducing a New Role into the Ember Programming Model

Ha, no I’m just glad someone else had the same idea and pushed it forwards, I really do think it solves a lot of the problems with the ‘needs’ approach.

I agree with the previous poster that this would make sense if service were clearly defined, but it really sounds like it’s definition is “an abstraction for everything that’s outside the abstracted roles defined so far” – which to me translates as an abstraction for the sake of abstraction. As someone who doesn’t program in Ember all day, but is trying to keep up while working jobs that are mostly in Rails + Angular, mainly cause I just sort of like Ember and would one day like to code in it professionally, it’s frustrating to turn away for a week and come back to yet another new role to learn. While all these conventions might theoretically make it easier for the everyday programmer to be more productive, honestly to me it just makes for a ridiculously steep learning curve to even get started with. How bout just take a break from new roles and give everyone a little bit of time to catch up?

@nathanhammond I have a feeling Tom was trying to elicit what a service is to the community, and from our actual uses… without “putting his fingers through it” by providing a definition. After all, frameworks should be extracted, right? :smile:

1 Like

Thought this might be relevant: Angular Classy

They are using needs: [...] for injection.

Thanks for the great discussion, everyone, and apologies for being away from the thread for so long. (I was caught up launching Skylight, where we’ve needed something like services for awhile.)

First, I’ll reiterate that I’d prefer to abandon the line of argument that hinges on offering the “maximally flexible” API. I am strongly against offering an arbitrary injection API that allows you to pull in any object from any other object.

I’ve really enjoyed reading the discussion around clarifying the role of services, especially vis à vis controllers. I think defining the roles of services by comparing and contrasting them to controllers is the right way to carry the discussion forward.

First, let’s discuss what exists right now. Even though routes and controllers are different things, in my head, they are both conceptually in the “controller layer.”

Let’s turn to Cocoa, which inspired many of the design decisions in Ember.

Cocoa has two types of controllers:

  • Mediating controllers
  • Coordinating controllers

According to Apple: “A mediating controller facilitates the flow of data between view objects and model objects.”

While

Coordinating controllers oversee and manage the functioning of an entire application, or part of one. They are often the places where application-specific logic is injected into the application. A coordinating controller fulfills a variety of functions, including:

  • Responding to action messages (which are are sent by controls such as buttons when users tap or click them)
  • Establishing connections between objects and performing other setup tasks, such as when the application launches

It’s obvious to me that Ember’s controllers are mediating controllers and route objects are coordinating controllers. Apple’s description of controllers in Cocoa maps almost perfectly onto what we have in Ember. The key difference is that, on the web, coordinating controllers have the additional responsibility of saving application state to the URL. (In Cocoa, the behavior of persisting and sharing state is done in an ad hoc way across different apps)

Perhaps Yehuda and I misnamed route objects when we created them, but our goal was to emphasize the fact that these were objects that set up the state of the application based on the URL. Perhaps we should have named them RouteControllers or something, but the time for that particular bikeshed has unfortunately come and gone. :wink:

On to services. Services, in my mind, are another specialized controller role.

If routes (coordinating controllers) control which templates and models are on screen
And controllers (mediating controllers) facilitate the flow between those templates and models
Then the role of services is to bridge the world of models and {mediating|coordinating} controllers.

Put another way, I see Ember Data’s store as a service and I want to, as they say in the standards bodies, provide features that “explain existing features.

My desired goal is to delete (almost) everything in ember-data/lib/initializers, change DS.Store to inherit from Ember.Service, and have everything continue working.

I would also like to easily add new services to my apps that make it easy for controllers and routes to marshal additional model data. For example, I’ve been working on an app for looking up bus arrival times in Portland called pdxb.us. One thing you need to calculate nearby arrival times, obviously, is the user’s location. I consider this location information to be “model” data just as much as the arrival times that come from TriMet’s JSON API.

As you can see, I defined a location service that gets injected into routes and controllers, then use that service in my route, just like how I’d use Ember Data’s store.

Hopefully this explanation helps you understand a little bit better my thinking behind services. I’m also not married to the name “service,” and am happy to entertain alternatives that may be clearer to new users.

TL;DR We currently have views, models, and coordinating controllers (routes). Mediating controllers (controllers) serve as the bridge between the view and controller layer; services should serve as the bridge between the model and controller layer.

15 Likes

Sounds good, I’ve got a few of these “services” in my app which are currently wired up in initializers and would be great for that to be automagic.

Eg.

  • queue - injected onto routes to publish background events to be run on the server
  • api - object to interact with our app’s REST API
  • store - using Fireplace, the same deal as Ember Data’s store

Another pattern I’ve been using to extract behaviour from the route is command objects which I talk about here which are kind of like services in that they go between the route and the model, but feel conceptually different in how they are used.

1 Like

I really like the direction @tomdale is headed with this. Giving services a clear purpose instead of trying to provide something that will be infinitely flexible goes a long way toward educating people how to use it.

I also like the example of geolocation as a service. We have a similar service in our app, but for desktop notifications. I’ve currently implemented it as a controller, though it never actually controls a view. It’s implemented as a controller so that other controllers can use it through dependency injection (i.e. “needs”).

Another example of a service we have in our app is a local cache for model objects. To continue the parallel with Cocoa, our model cache functions more or less like CoreData’s managed object context, but more lightweight. Basically, it manages object instances to make sure that controllers that need them always get the same instance. It also takes care of setting up and tearing down model objects when they aren’t needed anymore. I assume it functions similarly to Ember Data’s Store class, though I haven’t had time to dive into Ember Data yet.

1 Like

@tomdale Thanks for linking the examples in the pdxb.us app. I wanted to implement a similar service layer in my own ember(-cli) project and the example code made it really easy to get going.

I struggled with using the more powerful inject and register methods, so personally I would love if services became more of a convention that could be officially documented.

I just found another example of something that would make a good service candidate in our app: localStorage.

Just thinking about this a bit more, I could see modeling many common capabilities of browsers as services that are able to smooth out the differences in implementation. For example, XHR, getUserMedia, etc.

This sounds good, however I’m concerned with unnecessarily bloating the API/file size. This seems like it doesn’t need to be directly baked into the framework, but rather an official Ember library for creating services.

Is Project Svelte still a thing? If we’re talking about adding new primitives I would also like to here how the size of the framework is going to get smaller, but at the same time the surface area of API is going to expand. That discussion probably needs to be in it’s own thread.

As linked to in the pdxb.us example, the actual implementation of this feature builds heavily on top of existing features. All said and done, I imagine it would be implemented in under 100 lines of code. Just moving the view infrastructure from Metamorph to HTMLbars will probably be an order of magnitude reduction compared to that.

2 Likes

I like the idea of defining a role for Services. I can see some good use-cases where I could use this in some applications I’ve built.

I am wondering if are there cases where services could depend on each other. So like in @tomdale’s example, but instead of a NearbyTweets controller we might have a more general Twitter service which in turn uses the Geolocation service. This example is a bit off, but you get the idea.

@tomdale when I see the word “services” I can’t help but thinking about the services menu concept in OS X. Is this concept analogous?

Services are features exported by your application for the benefit of other applications. Services let you share the resources and capabilities of your application with other applications in the system.

I think it we substituted the word applications with the word controllers we might have an approximation of what services are about in an ember context.

@tomdale I’d really like to see an architecture overview such as this but fleshed out more appear in the ofifcial ember docs. It really needs it.

Good clarification! This would make sense in our app too.

We’ve got something similar (albeit not named ‘services’) wrapping the W3C Geo API and device APIs (e.g. the push notification permissions API on iPhone).

I’m interested in how this fits into the world of (ES6) modules.

I like that Ember manages the Route and Controller instances automagically. The application, routes and controllers are all somewhat tightly coupled to one another, and this approach allows me to declare how my application works, rather than taking a more imperative approach.

A “service” seems like it could (should?) exist independently of the application. Given that, maybe the best advice would be to wrap the service up as a module that can be imported wherever it’s needed. The module loader already handles dependency management for loosely-coupled modules, so maybe it makes sense to just use that.

I’m not sure how this maps to “globals”-style Ember apps. Perhaps the best advice there is to tack service-providing objects onto the app namespace or the app controller.

(I apologize if I’m missing the point here, in that all of this “how” may be irrelevant in deciding whether there should be some class of objects in Ember called Services.)

1 Like

As a though-experiment, I forked @tomdale’s pdxb.us app and made the geolocation service into a stand-alone module.

https://github.com/nonsensery/pdxb.us/commit/5ea51219476c64d955f402741944a5c4d604cf11

This works in this instance, because the geolocation service doesn’t have any dependencies on other parts of the system, and it’s stateless. But how would you accomplish something like Ember Data’s store, where it needs to both maintain a cache and lookup models the user has defined?

Hey, so what’s the status on this? Can we play it with right now? I do like the idea of some sort of service object.

1 Like

I submitted a PR, but after discussion in that thread, the proposed API has changed and I have not yet updated the PR to reflect that. If you’d like to tackle it, feel free to fork the PR and update it with the new API.