Best Practices for Web Service Design with Ember Data


#1

Hello,

I am in the process of designing the web service backend that our Ember-Data app will consume, and I have a few questions on best practices here. I did not find a related post, but if I missed it, please let me know.

Responses on many, all or just one of the below items are welcome!

1. Extensive Use of Database Views?

At first, I assumed the models in Ember-Data should basically match 1-to-1 with the tables in our database, but increasingly I’m seeing that my frontend is more of a “subset” of the entire data model than a total and complete reflection. This leads me to conclude that my back end (web service) should be returning a whole lot of database views, so that I can get exactly the data I want for reporting and other screens.

Is it common to make extensive use of database views, rather than matching the models 1-to-1?

2. Composite Primary Keys

Many of our entities on the backend could be designed to use composite primary keys where instead of a single id column as the primary key, the primary key would be a composite of personId, and versionId or something like that. But Ember-Data seems to prefer single-key primary keys, and the only way around this seems to be to write a custom adapter on a per-model basis, which seems kind of painful.

Is there a clean / streamlined way of handling composite primary keys in Ember-Data, or am I better off just using a single key primary key wherever possible?

3. Preventing the Local Store from Getting Overloaded

Our app will sometimes return thousands of rows of data for a given query. We’re throttling this by using pagination at every step so that while the database query might return 1,000 rows, the web service will return only 50 rows.

But let’s say the user keeps clicking through the pages, say, 20 pages. We’ve now downloaded 1,000 records of data, and if these are large objects, now I’m starting to worry about filling up the local store with too many objects. Does Ember Data automagically manage memory for me like this and will “throw away” older data and just request it from the server again if I need it? Or do I need to manually manage this somehow?

4. Being Careful with Foreign Key References

Say, I have the following models defined:

var User = DS.Model.extend({
  username: DS.attr('string')
});

var Person = DS.Model.extend({
  firstName: DS.attr('string'),
  lastName: DS.attr('string'),
  user: DS.belongsTo('user')
})

Now say I have a page that shows all users together with their first name and last name. I might do a query like:

this.store.find('person');

and in my Handlebars Template I might do something like:

{{#each person in controller}}
   {{person.firstName}} - {{person.user.userName}}
{{/each}}

Am I correct in understanding that Ember-Data will blindly look for the right user_id when it encounters {{person.user.userName}}, which it will do by first checking the store, and if not found there doing one web service call for each instance of a user, which could be 50 separate web service calls for a row of 50 person records?

Basically, what I’m getting at is, what’s the best way to manage these foreign key relationships without incurring major network traffic? My thought right now is, per #1, to use a whole lot of database views, so that I get all the data I need from the web service in one fell swoop?

Summary

This is a long post, so thanks for reading this far. It’s been a blast working with Ember and Ember-Data so far and I really appreciate the helpfulness of this community.

Josh


Nested dynamic segments using RestAdapter
#2

Based on what I’ve done at work:

  1. Most of our client-side models are simplified versions of the server-side models. In some cases the server flattens several tables into one abstraction which it serves to the client. This seems reasonable to me, as the client should only see what it needs.

  2. We have a few places where we serve models that are best identified using composite keys, but we present the model through the REST API using a primary key that concatenates the primary key values with a hyphen. Ember Data and the JSON API are suited to requesting/updating/deleting records using a single ID, which can be a string.

  3. Ember Data doesn’t automatically purge cached models. Whenever the user navigates away from a route (see the Route’s deactivate hook) where lots of data may have been cached, which is no longer needed, I purge those records from the cache. Specifically I filter over all the items of a type for just those that are not referenced by any other models in the cache. Don’t want to break dependencies. Use the store.unloadRecord method, and be sure to rollback first if the models may be dirty.

  4. Dependent records (belongsTo or hasMany) will be requested immediately after Ember Data gets a model referencing records not in the store. You can sideload the records that will be needed, by adding additional top-level fields in the JSON payload response, and Ember will add them to the store so they won’t be requested separately, e.g. responding to find('person')

{
	people: [
		{id: 1, user: 101, ...},
		{id: 2, user: 102, ...}
	],
	users: [
		{id: 101, ...},
		{id: 102, ...}
	]
}

Hope someone else chimes in, as I’m sure there are other ways, but we’re comfortable with the approach we chose.


#3

macu,

Thanks for this great response. Really helps to clarify my thinking. I agree, will be interesting to hear how others address some of these issues.

Josh


#4

IMO, Ember Data doesn’t necessarily play nice with schemas made by the discerning data designer.

So, as in any case where you have two systems with disparate interfaces, use the adapter pattern. Take the request being thrown out by Ember Data, take what the correct query to your database would be, and use your server-side scripts to match them up. Views would be one way to do this (Procedures would be my choice). In general though, I don’t like goofing with a good database design to fit a particular application, and it’s easier to work with and transform data closer to the source.

So, let Ember Data do it’s goofy REST-ish requests. Design your database like a grownup. Use server-side scripting to make them match up.

Alternative response: Ember data is a lot more fun if you denormalize your database enough to match what it’s expecting.

Welcome to the bleeding edge! =-)