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
- 232