🚀 See the 2024 Ruby on Rails Community Survey results!
Article  |  Development

Simplicity vs. Maintainability: "One Model, One Endpoint" with Ember Data

Reading time: Less than a minute

Early in our Ember development we came to a crossroad that had significant impact on the Model structure in our application.

Here at Planet Argon we have traditionally been a Rails shop. We had built features with Backbone.js and had plenty of spaghetti jQuery/JS to go around, but this was our first opportunity to work with a full-featured JS framework. We chose Ember/Ember-Data and after the well publicized learning curve came to really appreciate everything it did to help make our lives easier.

In this post I’ll summarize one of many issues we ran into during those first several months, and solicit feedback in search of a "best practice" for the Adapter/Serializer structure in the following scenario. ​ Like many issues, this was driven by API structure, supported by the ease of creating singular model/adapter/serializer packages for individual endpoints in Ember Data. ​ Our client needs a complex, multipart form that requests live estimates from the API. These requests happen each time the user touches a form field, displaying the response immediately, along with any resulting errors returned from the server.

Our application’s model structure is built around a parent 'Project' model consisting of multiple child objects, 'Assembly' and 'Fabrication', each with a collection of possible 'Turn Times'.
​ The path of least resistance when working with Ember Data appeared to be a single model for each endpoint. Working with this "One Model, One Endpoint" philosophy, we were able to create smaller adapters/serializers that were individually more maintainable as our API morphed and changed.

Since the API was also in active development, but by a different team, maintainability was especially important. ​ I still haven’t answered a nagging question to my satisfaction: Was this simplicity worth the added model complexity?

Some background:

//API Structure
// Base Project Endpoint:
GET: \/project\/:project_id

// Child Submission & Retrieval Endpoint:
GET | PUT | POST: /project/:project_id/assembly
GET | PUT | POST: /project/:project_id/fabrication
​
// Pricing Endpoint (returns Turn Times):
POST: /project/:project_id/assembly/pricing
POST: /project/:project_id/fabrication/pricing

// Desired Model JSON Structure
{
  project: {
    assembly: {
      turnTimes: []
    },
    fabrication: {
      turnTimes: []
    }
  }
}
​
//Actual
{
  project: {
    assembly: {},
    assemblyPricing: {
      turnTimes: []
    },
    fabrication: {},
    fabricationPricing: {
      turnTimes: []
    },
  }
}

​ Since assembly and assemblyPricing are essentially copies of one another, copying the desired pricing object to its base record for final submission was a pretty simple transition, but it still feels like an avoidable step. ​ How would you handle this scenario? Should adapters and serializers strive to maintain clarity and maintainability via the "One Model, One Endpoint" rule of thumb? Or should the working JSON model within the application be as simple as possible, leveraging adapters and serializers to deliver similar objects to multiple endpoints?

Have a project that needs help?