Active Record Pattern

Concepts

Active Record Pattern

The Active Record Pattern accomplishes similar goals as the Data Mapper Pattern, but with a slightly different implementation. While the Data Mapper Pattern calls for one Mapper instance per database view/table/collection, the Active Record Pattern wraps each database view/table/collection into a class, with instances of that class corresponding to individual "smart" records in each view/table/collection. These smart records are capable of saving and destroying themselves. JSData's default approach is a combination of the Data Mapper Pattern and the Active Record Pattern.

👍

Tip

While Mapper instances can work with POJOs, by default records will be wrapped in the Record class, turning them into "smart" records.

Here's are some quick examples to illustrate:

// or import {DataStore} from 'js-data'
import {Container} from 'js-data'
import adapter from './adapter.js'

// or const store = new DataStore()
const store = new Container()
store.registerAdapter('myadapter', adapter, { default: true })

// Mapper instance (Data Mapper Pattern)
store.defineMapper('post')

// Create and save a new post record using the Data Mapper Pattern
store.create('post', {
  title: 'Conceptual Overview of JSData'
}).then((post) => {
  console.log(post.id) // 1234

  // Create and save a new post record using the Active Record Pattern
  const post = store.createRecord('post', {
    title: 'Components of JSData'
  })
  return post.save()
}).then((post) => {
  console.log(post.id) // 1235
})
// or import {DataStore} from 'js-data'
import {Container} from 'js-data'
import adapter from './adapter.js'

// or const store = new DataStore()
const store = new Container()
store.registerAdapter('myadapter', adapter, { default: true })

// Mapper instance (Data Mapper Pattern)
store.defineMapper('post')

// Update a post record using the Data Mapper Pattern
store.update('post', 1234, {
  status: 'published'
}).then((post) => {
  console.log(post.id) // 1234
  console.log(post.status) // "published"
  console.log(post.published_at) // undefined

  // Update a post record using the Active Record Pattern
  post.published_at = new Date()
  return post.save()
}).then((post) => {
  console.log(post.id) // 1234
  console.log(post.status) // "published"
  console.log(post.published_at) // "2016-04-18T15:20:53.949Z"
})
// or import {DataStore} from 'js-data'
import {Container} from 'js-data'
import adapter from './adapter.js'

// or const store = new DataStore()
const store = new Container()
store.registerAdapter('myadapter', adapter, { default: true })

// Mapper instance (Data Mapper Pattern)
store.defineMapper('post')

// Destroy post record using the Data Mapper Pattern
store.destroy('post', 1234).then(() => {

  // Destroy a post record using the Active Record Pattern
  const post = store.createRecord('post', {
    id: 3554
  })
  // post with id 3554 is destroyed from the database
  return post.destroy()
})

Some folks like the convenience of smart records, because as they pass record instances around in their application they can easily create, update, and delete records without turning to a Mapper instance.

One tradeoff to "smart" records is that the extra behavior provided by the class interface takes up property names that you might otherwise use as your column names.