【问题标题】:Directory/Namespace Structure for versioned REST API版本化 REST API 的目录/命名空间结构
【发布时间】:2016-10-21 10:45:33
【问题描述】:

我已经阅读了很多对 RESTful API 的 URI 进行版本控制的最佳实践,例如(http://api.example.com/v1/users -> http://api.example.com/v2/usersHATEOAS),但对我的代码库中的目录或命名空间结构知之甚少( PHP+silex 框架)。 我的代码库现在可以做什么:代码库本身支持 API 的多个版本,通过路由或 Accept 标头识别版本,并且可以根据识别的 API 版本调用不同的控制器/类/方法(例如在 v1 UserController::listUsers() 中,在v2:UserControllerV2::getListUsers())。 随着时间的推移,API 会有越来越多的版本,但在某些时候旧版本应该从代码库中删除。

所以问题是

  • 应该对哪些类进行版本控制? (控制器、模型、视图等...)
  • 当您涉及到捆绑领域驱动设计时,如何做到这一点? (版本完整的包目录或只是在包内?)
  • 它们应该如何进行版本控制? (类继承(如何?),目录结构......)
    • 代码重复更少,
    • 轻松删除旧版本
    • 如果您修复版本之间共享的问题,则副作用更少

当前src 目录结构是例如(public, vendor 高一级):

   .
   └── TestNext
       ├── ApiV1
       │   └── Route
       │       └── ApiV1RoutesProvider.php
       ├── Configuration
       │   ├── Controller
       │   ├── Loader
       │   │   └── YamlConfigLoader.php
       │   ├── Model
       │   └── Service
       │       └── SymfonyConfigServiceProvider.php
       ├── Security
       │   └── Authenticator
       │       └── TokenAuthenticator.php
       ├── Shared
       │   └── Controller
       │       └── BaseController.php
       ├── User
       │   ├── Controller
       │   │   └── UserController.php
       │   ├── Model
       │   └── Service
       ├── Bootstrap.php
       ├── Console.php
       └── Constants.php

【问题讨论】:

    标签: php api rest domain-driven-design versioning


    【解决方案1】:

    首先,将概念分开是值得的。你有你的域,你有一个 API 层。分层之后,您的 API 层应该位于您的域之上(并且“分离”),并且您的域应该完全不知道 API 的存在。它有助于围绕此构建事物,一种方法如下:

    src/Acme/Api/
    src/Acme/Core/
    

    API 中的所有内容都处理 HTTP 级别的通信;路由、请求和响应映射、状态码等

    Core 中的所有内容都处理与业务相关的操作。遵循 CQRS 风格的方法,您可以以如下方式结束:

    src/Acme/Api/Controller
    src/Acme/Api/DTO/Request/
    src/Acme/Api/DTO/Response/
    src/Core/Domain/
    src/Core/Command/
    src/Core/CommandHandler/
    src/Core/Infrastructure/
    src/Core/ReadModel/
    

    但实际上,布局和命名将是灵活的,这在一定程度上取决于您应用的架构模式。在 DDD 上下文中,关键是您将聚合、模型、值对象和存储库放在某个公共命名空间 (Domain) 下。

    解决您的个人问题:

    应该对哪些类进行版本控制? (控制器、模型、视图等...)

    在我看来,版本模型没有意义。模型应该始终是业务的最新表示,维护规则似乎没有必要。

    如何处理版本控制取决于您。您可以将其视为对整个 API 进行版本化的一种方式(路径 + 请求/响应有效负载),或者只是请求/响应有效负载。完全对 API 进行版本控制可能是最灵活的,但进行如此巨大的更改相对很少见。您可能需要考虑利用 Accept / Content-Type 标头在每个请求级别上进行版本控制。您甚至可以将两者结合使用(URL 中的主要版本凸起来重新定义路径并强制使用特定的 Content-Types 版本)。

    理论上,您可以更进一步,对您的 JSON 架构进行版本控制,它定义了您的请求/响应负载。举个例子:

    GET /1.0/user/317684e2-3704-11e6-8172-b0bea8105888/payment/2caad76e-3705-11e6-8172-b0bea8105888
    Accept: application/vnd+acme+json; schema=payment.out.v1.json
    
    HTTP/1.1 200 OK
    Content-Type: application/vnd+acme+json; schema=payment.out.v1.json;     charset=UTF-8
    
    {
      "payee": "Bob Dylan",
      // snip
    }
    ```
    

    如果您想对响应负载引入非向后兼容的更改,那么您可以允许客户端针对“v2”JSON 模式进行付款请求:

    GET /1.0/user/317684e2-3704-11e6-8172-b0bea8105888/payment/2caad76e-3705-11e6-8172-b0bea8105888
    Accept: application/vnd+acme+json; schema=payment.out.v2.json
    
    HTTP/1.1 200 OK
    Content-Type: application/vnd+acme+json; schema=payment.out.v2.json; charset=UTF-8
    
    {
      "payee": {
        "forename": "Bob",
        "surname": "Dylan"
      },
      // snip
    }
    

    主要 API 版本不变,但响应负载不同。您可以及时弃用旧版本,或者确实提升主要 API 版本并在有效负载级别设置最小值。

    通过利用Content-Type 标头,可以将相同的技术应用于请求正文。

    当您涉及捆绑领域驱动设计时,如何做到这一点? (版本完整的捆绑目录或只是在捆绑中?) 他们应该如何进行版本控制? (类继承(如何?),目录结构...)

    也许它已经回答了,但尽量不要从 Symfony 样式包的角度来思考。你的Core,或者任何你想称呼它的名字,都应该没有特定的 Symfony/Silex。在Infrastructure 中,您可能有Repository 接口的实现,可能使用了Doctrine,但那是我们利用Doctrine 作为库,而不是依靠Symfony/Silex 作为框架。如果你做得好,理论上你可以将 API 层换成一个完全不同的框架,而不需要对 Core 进行任何更改。

    虽然有一些不可避免的事情:依赖注入和配置。过去,我使用 Symfony 创建了一个 CoreBundle 来解决这个问题。它位于Core 之外。

    顺便说一句,在这样一个项目中只包含一个有界上下文可能是明智的,所以不要担心进一步分类。

    代码重复更少,

    重复发生是不可避免的,但是当这种情况只适用于您的 DTO 时,因为您不会对定义行为的对象(您的模型)进行版本控制。 DTO 中的重复并不是什么大问题。如果获得的清晰度多于失去的清晰度,那么只需为每个版本定义一个类。工具可能会告诉您代码已被复制粘贴,但这些工具无法理解此类上下文。

    轻松删除旧版本

    如果您定义单独的 DTO,那么这样的任务应该很简单。删除 DTO,删除 JSON 模式,现在您的 API 应该拒绝指定旧版本的请求。

    如果您修复版本之间共享的问题,则副作用更少

    如果您要映射到 API 层中的请求/响应对象,那么您的映射代码可能会受到模型更改的影响。良好的测试覆盖率(通过 API 合同测试、进程内组件测试和单元测试)应该验证对模型的更改不会导致所有版本的 API 表现不同。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-11-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-03
      相关资源
      最近更新 更多