Sommaire
  1. Travail préparatoire
  2. Répertorier les controllers & actions qui seront présent sur la version GraphQL de l’API
  3. Extraire les actions des controllers et les transformer en objet
  4. Mettre à jour sa couverture de test
  5. Conclusion

Cet article fait suite au précédent, sur le thème de la migration d’une API REST vers GraphQL.

Dans l’article précédent, nous avons survolé le plan d’action mis en place pour faire cette migration.

Nous allons voir dans ce billet la phase de préparation plus en détails. Tout d’abord, un petit rappel :

Travail préparatoire

  • Répertorier les controllers & actions qui seront présent sur la version GraphQL de l’API.
  • Extraire les actions des controllers et les transformer en objet (design pattern command).
  • Optionnel (mais vivement conseillé !) : mettre à jour sa couverture de test.

Répertorier les controllers & actions qui seront présent sur la version GraphQL de l’API

Pour cette phase, vous seul êtes à même de répertorier quelles actions sont utilisées ou non par les consommateurs de votre API.

Pour la méthodologie, une passe exhaustive sur l’ensemble des controllers est nécessaire.

Vous pouvez également en profiter pour prendre des notes sur les travaux de refactors à prévoir, les tests unitaires à écrire... Ceci vous permettra de commencer à chiffrer plus précisément le temps à passer pour les futures étapes.

Extraire les actions des controllers et les transformer en objet

Je vous conseille tout d’abord de prendre connaissance du design pattern Command (ou Service Object )

Le but de cette étape est d’extraire chaque action de vos controllers en objet. En procédant de cette manière, plusieurs avantages se dévoilent :

  • Vous pourrez identifier les dépendances de votre code ;
  • Vous pourrez tester la logique du code plus facilement, indépendamment du contexte du controller ;
  • Vous pourrez ré-utiliser votre code à plusieurs endroits ;

Sur l’exemple suivant, on va extraire la logique de l’action create dans le controller posts_controller.rb.

Avant refactor :

## app/api/v1/posts_controller.rbmodule Api  module V1    class PostsController < ::Api::ApplicationController      def create        ## Logique métier        ## ...        ## ...        if post_repository.save(post)          redirect_to post_path(post)        else          render "new"        end      end      def post_repository        @post_repository ||= ::Repositories::Post.new      end    end  endend

Après refactor :

## lib/command/post/create.rbmodule Command  module Post    class Create      def self.exec({ attrs, callbacks, repositories })        new(callbacks, repositories).exec(attrs)      end      def exec(attrs)        ## Logique métier        ## ...        ## ...        if @repositories[:post].save(post)          @callbacks[:success].call(post: post)        else          @callbacks[:failure].call(post: post)        end      end      private       def initialize(callbacks, repositories)         @callbacks = callbacks         @repositories = repositories       end     end  endend
## app/api/v1/posts_controller.rbmodule Api  module V1    class PostsController < ::Api::ApplicationController      def create        ::Command::Post::Create.exec(          {            attrs: params,            callbacks: {              success: ->(args) {                @post = args[:post]                redirect_to post_path(@post)              },              failure: ->(args) {                @post = args[:post]                render "new"              },            },            repositories: {              post: ::Repositories::Post.new,            }          }        )      end    end  endend

Voici quelques liens qui pourront vous êtes utile pour aller plus loin :

Mettre à jour sa couverture de test

Avant toute modification ou refactor, je vous conseille d’écrire & de mettre à jour vos tests. Ceci vous permettra de “tout casser” sereinement !

Je ne vais pas m’attarder sur la mise en place des tests unitaires, mais je vais vous présenter un exemple adapté aux changements.

Si vous utilisez rspec, l’exemple ci-dessous pourra vous servir de template afin de tester de vos Objets command.

require 'rails_helper'RSpec.describe ::Command::Post::Create do  let (:callbacks) {    {      success: -> (*args) { :success },      failure: -> (*args) { :failure }    }  }  before do    post_repository = double('::Repositories::Post')    allow(post_repository).to receive(:save).with(an_instance_of(::Post)).and_return(true)    @repositories = {      post: post_repository,    }  end  let (:command_post_create) { ::Command::Post::Create }  describe "create" do    context "the record is created" do      let (:attrs) {        {       title: "Test"    }      }      it "calls success callback" do        result = command_post_create.exec(         attrs: attrs,          callbacks: callbacks,          repositories: @repositories        )        expect(result).to eq :success      end    end  endend

Voici quelques liens qui pourront vous êtes utile pour aller plus loin :

Conclusion

Après ce travail, vous devriez avoir séparé la logique métier des actions des controllers dans des objets spécifique. Ceux-ci, ont leurs dépendances bien identifiées, elles vont pouvoir être testés correctement.

Vous pouvez désormais utiliser ces objets n’importe où dans votre projet, ce qui évitera de dupliquer le code entre vos deux versions d’API.

Vous avez donc dès à présent une bonne valeur ajoutée en termes de clarté et de structuration !