Versionamento de API RESTful em PHP 8.2 — (Sim, vc não leu errado, PHP kkj)
Vou ser simples e direto. Estou desenvolvendo uma API RESTful usando PHP 8.2, aplicando alguns design patterns, incluindo MVC (estou considerando as responses JSON como view, kkkj) e estruturando tudo com POO.
Durante esse processo, surgiu uma dúvida importante: como garantir que, ao alterar um recurso da API, os sistemas consumidores não quebrem sem qualquer feedback?
Bom... a resposta eu já imaginava qual seria, mas isso me trouxe ainda mais perguntas.
Como muitos já devem saber, existe uma convenção sobre versionamento de APIs REST, os famosos v1, v2, v3... (você pode versionar até onde der, kkk). Até aí, tudo certo. Mas a grande questão que surgiu foi: como estruturar meu projeto de forma que seja fácil de manter?
Por exemplo, simplesmente configurar o Router para suportar v1, v2, etc. não resolve o problema por completo, pois ainda seria necessário versionar todo o CRUD (sim, meu CRUD kkkj).
Vi um vídeo onde o autor versionava os métodos das entidades, acrescentando "V1" ao final dos nomes. A ideia pareceu interessante à primeira vista, mas me fez questionar se essa era realmente a abordagem ideal.
Uma alternativa que considerei foi criar uma pasta "v1", onde os Routers iriam apontar. Em seguida, eu faria uma cópia exata da v1, nomearia como "v2", e então realizaria todas as modificações necessárias nela. Depois, apontaria novos endpoints v2. Dessa forma, daria tempo suficiente para o client-side migrar para a nova versão sem problemas, e eu evitaria transformar o código em uma zona (na minha cabeça, pelo menos, kkkj).
Porém, ainda acho que pode existir uma abordagem mais eficaz. E quando digo eficaz, não me refiro a uma maneira "certa" ou "errada" de fazer, mas sim a um método que proporcione:
✅ Melhor legibilidade do código
✅ Facilidade na manutenção
✅ Uma documentação clara (preciso documentar para o client, né? kkkj)
E aí, qual abordagem você acha mais interessante? 🤔
Cara, usa Laravel. Você pode criar agrupar as rotas com prefix e name e só criar diretórios diferentes para cada versão, e manter os mesmo nomes das classes, só que em namespaces diferentes, não tem complicação nenhuma.
// Dentro do arquivo routes/api.php
// importação da rota v1
use App\Http\Controllers\Api\V1\ProdutoController;
use App\Http\Controllers\Api\V2\ProdutoController as ProdutoControllerV2; // alias por ter o mesmo nome da classe acima
// versão 1
Route::prefix('v1')->name('v1.')->group(function(){
// exemplo de rota de produtos
Route::apiResource('produtos', ProductController::class); // (dominio.com.br/api/v1/produtos) - rota interna (api.v1.produtos)
});
// versão 2
Route::prefix('v2')->name('v2.')->group(function(){
// exemplo de rota de produtos
Route::apiResource('produtos', ProductController::class); // (dominio.com.br/api/v2/produtos) - rota interna (api.v2.produtos)
});
Se quiser deixar mais organizado ainda, você pode separa os arquivos de rotas por versão e daí nem precisa fazer o alias. Dentro do seu diretório ficaria assim:
- /routes/api.php
- /routes/api/v1.php
- /routes/api/v2.php
Aí no arquivo/routes/api.php
// versão 1
require __DIR__ . '/api/v1.php';
// versão 2
require __DIR__ . '/api/v2.php';
Dentro da v1, por exemplo:
// importação da rota v1
use App\Http\Controllers\Api\V1\ProdutoController;
// versão 1
Route::prefix('v1')->name('v1.')->group(function(){
// exemplo de rota de produtos
Route::apiResource('produtos', ProductController::class); // (dominio.com.br/api/v1/produtos) - rota interna (api.v1.produtos)
});
Dentro da v2, por exemplo:
// importação da rota v2
use App\Http\Controllers\Api\V2\ProdutoController;
// versão 2
Route::prefix('v2')->name('v2.')->group(function(){
// exemplo de rota de produtos
Route::apiResource('produtos', ProductController::class); // (dominio.com.br/api/v2/produtos) - rota interna (api.v2.produtos)
});
Meus 2 cents:
Respondendo a pergunta especifica: como manter o codigo de cada versao (v1, v2, etc) ?
Quando tive essa necessidade, foi mantendo como voce comentou: uma pasta para cada versao especifica e o roteador (p.ex.SLIM) apontado para ela, usando o "use v1/crud as crudV1; use v2/crud as crudV2" para identificar as funcoes desejadas, usando um namespace diferente para cada uma.
|-api
|-v1
- crud da v1
|-v2
- crud da v2
Exemplos de namespace neste caso na propria documentacao do PHP: Defining multiple namespaces
Semelhante ao caminho apontado pelo @silvestrini no outro comentario.
Pelo que entendi, você está usando PHP puro, vai usar que router pra cuidar disso?
Minha sugestão é que você use Laravel, com o framework, já vai ter muita coisa pronta pra vc colocar isso pra rodar rápido.
Outra coisa é que você deve atualizar a versão do PHP que você está usando. Já estamos na versão 8.4.