为你的对象和你的反应建模


Models for your your objects and also for your responses

我开始在一个新的体系结构范式中开发一些遗留代码。让我们把不同的范式称为v1和v2。

在v1上,我们有不同域(例如游戏)的模型,这些模型嵌套了实体和属性的所有信息——多次混合了实体是什么和属性是什么。因此,当您到达一个端点时,您可以得到一个嵌套与域相关的所有内容的响应。E.g/v1/游戏会给你带来游戏+玩家+团队+竞技场。

在v2上,我们试图更加面向资源,这样你就可以真正获得你要查询的资源(例如)-你有一个v2/游戏的端点,然后你就会有一个v2/游戏/{id}/玩家的端点。前者会得到游戏对象,而后者会得到玩家对象。顺便说一句,我认为这是一部自我纪录片——你知道看看你提出的请求会得到什么。

在v1上,我们有一个巨大的模型,其中包含了所有内容——无论我们认为什么是域的"属性"。因此,我们的实际模型将包括相关"属性"的对象——例如,游戏对象将包含玩家对象数组。

无论何时你获取或发布消息,你实际上都会使用相同的模型,你只会在get上获得更多信息(嵌套对象),并且POST不需要一些字段(例如,你不需要将帖子发送给玩家,我们有一个单独的端点)。

class Game {
    public $id;
    public $datePlayed;
    public $players = [];
    public $homeTeam = [];
    public $awayTeam = [];
}

在v2模型上,我们对数据库表进行实际表示(就像建模的主动记录方法一样,模型是基于持久层的)。因此,游戏模型将不包括玩家(因为这完全是一个不同的对象)。

class Game {
    public $id;
    public $datePlayed;
    public $homeTeamId;
    public $awayTeamId;
}

到目前为止,我认为最终结果非常好,因为我们实际上得到了我们需要的资源,而不是一个有很多冗长和不必要数据的嵌套版本。

然而,我现在有一个新的挑战。有些端点需要返回包含更多信息的响应,而这些信息将不是实际的数据库表示。由于我们不是在一个完美的世界上,我不希望使用我们的API的开发人员对所有属性提出大量请求,我需要想出一个解决方案,帮助我向他们提供相关资源的一些基本信息。

因此,例如,返回我们的游戏端点->创建时,我确实希望他们向我发送一个有效载荷,即确切的数据库对象

{ 
    "datePlayed": "2016-04-29T17:20:08+00:00", 
    "homeTeamId": 5, 
    "awayTeamId": 15 
}

但对于GET,我想用基本数据来装饰响应(不是整个对象,但只是足够让开发人员获得基本信息,这样他们只会发出新的请求来获得完整的相关资源,而不是基本信息)。

{ 
    "datePlayed": "2016-04-29T17:20:08+00:00", 
    "homeTeam": {
        "id": 5,
        "name": "Killer clan"
    }, 
    "awayTeam": {
       "id": 15, 
       "name": "Retry team"
    } 
}

起初,我将这一职责委托给controller->repository,后者将根据请求创建一个新对象,并在GET方法上返回,但会将POST和PUT请求验证为实际模型。然而,从长远来看,这似乎不太容易维护,因为你必须深入研究代码才能理解你要发送的内容。

所以我想了很久,我有了一个想法,用惯例Stub(支票、收据、票证或其他文件的一部分,撕下来作为记录)创建一组新的模型,以声明响应的结构。例如:/我会创建一个类似的GameStub模型类

class GameStub {
    public $id;
    public $datePlayed;
    public $homeTeam = [];
    public $awayTeam = [];
}

我认为占位符或元数据也是不错的名称约定,但要点是一样的。

你们觉得这个解决方案怎么样?这有道理吗?

非常有意义,因为它是将应用程序与api解耦的好方法。

API中的资源!=应用程序模型。

我在应用程序中使用以下结构:

型号-游戏(这是你的模型)Param-GameParam(您希望通过api提供的模型/Param/Resources)转换器-GameConverter(将模型转换为参数,将参数转换为模型)

其中的一部分很有意义,但我不理解你的存根隐喻以及它与问题的关系。

所以GET/games/gameId通常只返回团队ID。为了获得更多的团队信息(如团队名称),用户将使用get/teams/teamId。您希望通过在游戏查询中发送一些(但不是全部)团队信息来避免额外的查询。

那么,您如何知道特定请求需要哪些附加信息呢?如果(例如)某个请求需要教练的名字怎么办?看起来你会试图猜测需要什么,最终再次发送所有内容。

你看过Facebook的图形查询语言吗?http://graphql.org/

基本思想是允许请求指定他们真正需要的信息。所以你可能有GET/games/gameId?want=teamName,coachName

然后由服务器决定如何返回必要的数据。我并不是建议在您的应用程序中使用graphql。这几乎可以肯定是夸大其词。

但也许给消费者一些指定他们需要什么的能力可能会有所帮助。

无论如何,利用一种称为命令查询责任分离的模式(http://martinfowler.com/bliki/CQRS.html)。任何与创建或更新对象有关的内容都应该在自己的空间中。独立处理查询。