Comment on a payé notre dette technique avec GraphQL

Luc Momal
Staff Engineer @

Luc Momal
Staff Engineer @

naissance d'un futur code legacy

Légende: Entité comme intérimaire, entreprise, contrat, facture ..

Notre histoire a commencé en 2015

Quelques années plus tard

J'ai pas les spaghettis.. bye bye !

Vas y, on reconstruit, tout ! 🥳

I don't like spaghetti.. bye bye !

3 mois
1 an plus tard

BIG BANG!!!!!

Synchronisation!!

BIG BANG!!!!!

BIG BANG!!!!!

BIG BANG!!!!!

GraphQL à la rescousse

GraphQL

GraphQL est un  PATTERN pas une technologie

 

GraphQL est langage pour  ton API

 

GraphQL est un remplacant REST

GraphQL

type Query {
  worker(id: ID!): Worker
}

type Worker {
  id: ID!
  firstName: String
  lastName: String
  contracts: [Contract]
}

type Contract {
  from: Date
  to: Date
  company: Company
}

type Company {
  name: String
}
query {
  worker(id: 42) {
   id
   firstName
   contracts {
     company {
       name
     }
   }
  }
}

GraphQL

type Query {
  worker(id: ID!): Worker
}

type Worker {
  id: ID!
  firstName: String
  lastName: String
  contracts: [Contract]
}

type Contract {
  from: Date
  to: Date
  company: Company
}

type Company {
  name: String
}
query {
  worker(id: 42) {
   id
   firstName
   contracts {
     company {
       name
     }
   }
  }
}
{
  worker: {
    id: 42,
    firstName: "John",
    contracts: [
      {
        company: {
          name: "cie"
        }
      }
    ]
  }
}

GraphQL

type Query {
  worker(id: ID!): Worker
}

type Worker {
  id: ID!
  firstName: String
  lastName: String
  contracts: [Contract]
}

type Contract {
  from: Date
  to: Date
  company: Company
}

type Company {
  name: String
}
query {
  worker(id: 42) {
   id
   firstName
   contracts {
     company {
       name
     }
   }
  }
}
{
  worker: {
    id: 42,
    firstName: "John",
    contracts: [
      {
        company: {
          name: "cie"
        }
      }
    ]
  }
}

GraphQL


type Query {
  worker(id: ID!): Worker
}

type Worker {
  id: ID!
  firstName: String
  lastName: String
  contracts: [Contract]
}

type Contract {
  from: Date
  to: Date
  company: Company
}

type Company {
  name: String
}
query {
  worker(id: 42) {
   id
   firstName
   contracts {
     company {
       name
     }
   }
  }
}
{
  worker: {
    id: 42,
    firstName: "John",
    contracts: [
      {
        company: {
          name: "cie"
        }
      }
    ]
  }
}
const resolvers = {
  Query: {
    worker: (parent, args, ctx) => db.worker.get(args.id)
  },
  
  Worker: {
    id: (parent) => parent.id,
    firstName: (parent) => parent.firstName,
    lastName: (parent) => parent.lastName,
    contracts: (parent) => db.contracts.getFromWorker(parent.id)
  },
  
  Contract: {
    from: (parent) => parent.from,
    to: (parent) => parent.to,
    company: (parent) => db.company.get(parent.companyId),
  },
  
  Company: {
    name: (parent) => parent.name 
  }
}

GraphQL


type Query {
  worker(id: ID!): Worker
}

type Worker {
  id: ID!
  firstName: String
  lastName: String
  contracts: [Contract]
}

type Contract {
  from: Date
  to: Date
  company: Company
}

type Company {
  name: String
}
query {
  worker(id: 42) {
   id
   firstName
   contracts {
     company {
       name
     }
   }
  }
}
{
  worker: {
    id: 42,
    firstName: "John",
    contracts: [
      {
        company: {
          name: "cie"
        }
      }
    ]
  }
}
const resolvers = {
  Query: {
    worker: (parent, args, ctx) => db.worker.get(args.id)
  },
  
  Worker: {
    id: (parent) => parent.id,
    firstName: (parent) => parent.firstName,
    lastName: (parent) => parent.lastName,
    contracts: (parent) => db.contract.getFromWorker(parent.id)
  },
  
  Contract: {
    from: (parent) => parent.from,
    to: (parent) => parent.to,
    company: (parent) => db.company.get(parent.companyId),
  },
  
  Company: {
    name: (parent) => parent.name 
  }
}

GraphQL


type Query {
  worker(id: ID!): Worker
}

type Worker {
  id: ID!
  firstName: String
  lastName: String
  contracts: [Contract]
}

type Contract {
  from: Date
  to: Date
  company: Company
}

type Company {
  name: String
}
query {
  worker(id: 42) {
   id
   firstName
   contracts {
     company {
       name
     }
   }
  }
}
{
  worker: {
    id: 42,
    firstName: "John",
    contracts: [
      {
        company: {
          name: "cie"
        }
      }
    ]
  }
}
const resolvers = {
  Query: {
    worker: (parent, args, ctx) => db.worker.get(args.id)
  },
  
  Worker: {
    id: (parent) => parent.id,
    firstName: (parent) => parent.firstName,
    lastName: (parent) => parent.lastName,
    contracts: (parent) => db.contracts.getFromWorker(parent.id)
  },
  
  Contract: {
    from: (parent) => parent.from,
    to: (parent) => parent.to,
    company: (parent) => db.company.get(parent.companyId),
  },
  
  Company: {
    name: (parent) => parent.name 
  }
}

GraphQL


type Query {
  worker(id: ID!): Worker
}

type Worker {
  id: ID!
  firstName: String
  lastName: String
  contracts: [Contract]
}

type Contract {
  from: Date
  to: Date
  company: Company
}

type Company {
  name: String
}
query {
  worker(id: 42) {
   id
   firstName
   contracts {
     company {
       name
     }
   }
  }
}
{
  worker: {
    id: 42,
    firstName: "John",
    contracts: [
      {
        company: {
          name: "cie"
        }
      }
    ]
  }
}
const resolvers = {
  Query: {
    worker: (parent, args, ctx) => db.worker.get(args.id)
  },
  
  Worker: {
    id: (parent) => parent.id,
    firstName: (parent) => parent.firstName,
    lastName: (parent) => parent.lastName,
    contracts: (parent) => db.contracts.getFromWorker(parent.id)
  },
  
  Contract: {
    from: (parent) => parent.from,
    to: (parent) => parent.to,
    company: (parent) => db.company.get(parent.companyId),
  },
  
  Company: {
    name: (parent) => parent.name 
  }
}

AMAZING!

GraphQL


type Query {
  worker(id: ID!): Worker
}

type Worker {
  id: ID!
  firstName: String
  lastName: String
  contracts: [Contract]
}

type Contract {
  from: Date
  to: Date
  company: Company
}

type Company {
  name: String
}
query {
  worker(id: 42) {
   id
   firstName
   contracts {
     company {
       name
     }
   }
  }
}
{
  worker: {
    id: 42,
    firstName: "John",
    contracts: [
      {
        company: {
          name: "cie"
        }
      }
    ]
  }
}
const resolvers = {
  Query: {
    worker: (parent, args, ctx) => db.worker.get(args.id)
  },
  
  Worker: {
    id: (parent) => parent.id,
    firstName: (parent) => parent.firstName,
    lastName: (parent) => parent.lastName,
    contracts: (parent) => db.contracts.getFromWorker(parent.id)
  },
  
  Contract: {
    from: (parent) => parent.from,
    to: (parent) => parent.to,
    company: (parent) => db.company.get(parent.companyId),
  },
  
  Company: {
    name: (parent) => parent.name 
  }
}

AMAZING!

Mmh, no!!!!

Il est temps de payer notre dette technique

Mmh, no!!!!

Migration du dernier composant de la v1

GET /workers/42/contracts
{
  worker: {
    id: 42,
    firstName: "John",
    lastName: "Doe",
    birthDate: "31/08/1989",
    //etc...
    contracts: [
      {
        company: {
          name: "cie",
          invoiceSettings: {},
          contacts: {},
          //etc...
        }
      }
    ]
  }
}

❌ Mmh, no!!!!

read only

GraphQL pour créer une nouvelle interface

REST call
read only

GraphQL pour repenser le domaine

read only

type Mission { 
   from: String
   to: String
   contracts: [Contract]
   company: Company
   worker: Worker
}

type Contract {
   type: String
   from: String
   to: String
}

{
    "company_name": "ABC Corp",
    "address": {
        "street": "123 Main St",
        "city": "Anytown",
        "state": "CA",
        "zip": "12345"
    },
    "invoicing_settings": {
        "currency": "USD",
        "tax_rate": 0.08,
        "invoice_terms": "Payment due within 30 days",
        "invoice_footer": "Thank you for your business!"
    },
    "jobs": [...],
    "contacts": [...],
    ...
}

Toutes les informations concernants les entreprises sont stockées

à un seul endroit dans la v1

GraphQL pour migrer


const Company = {
    "company_name": "ABC Corp",
    "address": {
        "street": "123 Main St",
        "city": "Anytown",
        "state": "CA",
        "zip": "12345"
    },
}

const CompanyInvoicingSettings = {
    "invoicing_settings": {
        "currency": "USD",
        "tax_rate": 0.08,
        "invoice_terms": "Payment due within 30 days",
        "invoice_footer": "Thank you for your business!"
    },
}

const CompanyJobs = {
    "jobs": [...],
}

const CompanyContacts = {
    "contacts": [...],
}

✂ Ces données peuvent être séparées en plusieurs morceaux.

GraphQL pour migrer

type Company {
    name: String
    street: String
    city: String
    state: String
    zip: String
    invoicing: CompanyInvoicingSettings
    job: CompanyJob
	contacts: CompanyContacts
}

type CompanyInvoicingSettings {
  currency: String
  tax_rate: String
  invoice_terms: String
  invoice_footer: String
}

type CompanyJob {
   title: String
   location: String
}

type CompanyContacts {
  name: String
  email: String
  phone: String
}

On peut facilement representer ce découpage sous la forme d'un schéma GraphQL

GraphQL pour migrer

GraphQL pour migrer

With Federation

you can merge

your graph! 🚀

GraphQL pour migrer

Avec Federation

on peut fusionner nos schémas graphQL! 🚀

GraphQL pour migrer

Merci !

Copy of How we have paid our debt with GraphQL

By Luc MOMAL

Copy of How we have paid our debt with GraphQL

  • 223