如何处理Flux应用程序中的嵌套API响应?

时间:2014-08-20 15:50:19

标签: json architecture nested cqrs flux

我正在将现有应用移植到Flux,我对一个主题感到有点困惑。 假设我有几个返回两级或三级嵌套对象的API端点。

例如,GET /articles可能会返回架构的JSON响应

articles: article*

article: {
  author: user,
  likers: user*
  primary_collection: collection?
  collections: collection*
}

collection: {
  curator: user
}

如您所见,不同级别的嵌套有各种各样的用户:

  • articles[i].author
  • articles[i].likers[i]
  • articles[i].primaryCollection.curator
  • articles[i].collections[i].curator

如果我想在任何时候提取文章时用新数据更新UserStore,我必须编写一个怪异的方法来检查文章API响应中的所有嵌套实体。此外,还会有很多重复,因为还有其他API端点具有不同的模式,有时文章嵌入在用户内部(例如GET /user/published)。

Flux商店是否有更清洁的方法从所有API响应中提取嵌套实体?

1 个答案:

答案 0 :(得分:8)

Jing Chen(Flux创作者和传播者之一)建议的方法是在API响应到达商店之前将其平铺。我写了一个小型库来做到这一点:它规范化了

[{
  id: 1,
  title: 'Some Article',
  author: {
    id: 1,
    name: 'Dan'
  }
}, {
  id: 2,
  title: 'Other Article',
  author: {
    id: 1,
    name: 'Dan'
  }
}]

{
  result: [1, 2],
  entities: {
    articles: {
      1: {
        id: 1,
        title: 'Some Article',
        author: 1
      },
      2: {
        id: 2,
        title: 'Other Article',
        author: 1
      }
    },
    users: {
      1: {
        id: 1,
        name: 'Dan'
      }
    }
  }
}

(注意没有重复,结构是平的。)

Normalizr 可以让您:

  • 将实体嵌套在其他实体,对象和数组中
  • 合并实体模式以表达任何类型的API响应
  • 自动合并具有相同ID的实体(如果它们不同则带有警告)
  • 使用自定义ID属性(例如slug)

要使用它,您需要定义实体和嵌套规则并使用它们来转换JSON:

var normalizr = require('normalizr'),
    normalize = normalizr.normalize,
    Schema = normalizr.Schema,
    arrayOf = normalizr.arrayOf;

// First, define a schema:

var article = new Schema('articles'),
    user = new Schema('users'),
    collection = new Schema('collections');

// Define nesting rules:

article.define({
  author: user,
  collections: arrayOf(collection)
});

collection.define({
  curator: user
});


// Usage:

// Normalize articles
var articlesJSON = getArticleArray(),
    normalized = normalize(articlesJSON, arrayOf(article));

// Normalize users
var usersJSON = getUsersArray(),
    normalized = normalize(usersJSON, arrayOf(user));

// Normalize single article
var articleJSON = getArticle(),
    normalized = normalize(articleJSON, article);

这允许您在将任何XHR响应传递给Flux Dispatcher之前将其标准化。 商店只需要从相应的字典中更新自己:

// UserStore

UserStore.dispatchToken = AppDispatcher.register(function (payload) {
  var action = payload.action;

  switch (action.type) {
  // you can add any normalized API here since that contains users:
  case ActionTypes.RECEIVE_ARTICLES:
  case ActionTypes.RECEIVE_USERS:

    // Users will always be gathered in action.entities.users
    mergeInto(_users, action.entities.users);
    UserStore.emitChange();
    break;
  }
});


// ArticleStore

AppDispatcher.register(function (payload) {
  var action = payload.action;

  switch (action.type) {
  // you can add any normalized API here since that contains articles:
  case ActionTypes.RECEIVE_ARTICLES:

    // Wait for UserStore to digest users
    AppDispatcher.waitFor([UserStore.dispatchToken]);

    // Articles will always be gathered in action.entities.articles
    mergeInto(_articles, action.entities.articles);
    ArticleStore.emitChange();
    break;
  }
});