📐 Construire une app en JS
15 Tdd

La réservation

Finissons-en avec la création de la réservation.

En programmation orientée objet, on opterait pour une classe Booking. Mais pour l'instant, nous avons seulement besoin d'une seule fonction Booking.of() en charge de la création d'une nouvelle réservation.

En bref, une sorte de constructeur.

Construire une réservation

Ce constructeur prend des paramètres.

const booking = Booking.of({
    tenantId:user.id, 
    accomodationId,
    hosts : {adults,children},
    interval : {from,to}
    });

On brûle d'envie de créer cette classe Booking, et de lui adjoindre plein de contrôles utiles dont nous auront forcément besoin plus tard :

  • vérifier que la date d'arrivée est dans le futur
  • contrôler que la date de départ est après la date d'arrivée

Mais aussi :

  • le logement demandé ne doit pas être déja loué
  • la taille du logement est limitée. Il ne peut pas accueillir plus de personnes que le max prévu.

Etc...

Nous sommes tous tentés de foncer à coder tout cela élégamment dans cette magnifique classe Booking.

Pourtant, je vous propose la démarche exactement inverse : on ne fait rien.

Ou plus exactement rien d'autre que le strict minimum nécessaire à faire passer notre test au vert.

Objectif : vert

Rappelons-nous notre test :

// Injection des dépendances
const app = new App(testDependencies())
 
// Réalisation de la location
await app.run([
    login({email:"faketenant@mail.com", password:"secret"}),
    book({
        accomodationId:"accomodation-1", 
        adults:2, children:3, 
        from : new Date("2024-06-02"),
        to :   new Date("2024-06-02"),
        })
]);
 
// Le calendrier comporte la réservation
const bookings = await app.dependencies.bookings.listBookingsForAccomodationId("accomodation-1")
expect(bookings).toHaveLength(1)

C'est lui qui guide notre démarche de conception. Comme une boussole.

Et que nous dit-il ?

Que n'importe qui peut réserver n'importe quoi.

Alors faisons le choix le plus simple pour construire notre objet et faire passer notre test.

const booking = {
    tenantId:user.id, 
    accomodationId,
    hosts : {adults,children},
    interval : {from,to}
    };

Pas besoin d'une fonction pour construire une réservation directement issue des paramètres, sans vérifier aucun invariant (pour l'instant !).

Cette méthode paraît complètement à l'opposé d'un processus d'ingénierie mûrement réfléchi dans lequel on code élégamment tous les cas possibles dont l'expert métier nous a parlé.

Que reste-il sur notre liste ?

  • les dépendances
  • la classe App
  • le constructeur de l'objet Booking appelé dans la fonction book()

Tout y est !

Il est temps de passer notre test au vert.


Quelles sont les règles métiers à respecter pour la création d'un réservation ?

Certaines sont évidentes :

  • le logement demandé ne doit pas être déja loué
  • la date d'arrivée doit être dans le futur
  • la date de départ doit être après la date d'arrivée
  • la taille du logement est limitée. Il ne peut pas accueillir plus de personnes que le max prévu.

Quoi d'autre ?

Discutons avec l'expert métier, et posons lui des questions.

  • Un vacancier qui a déjà une réservation pour un logement peut-il en faire une autre sur une même période ?
  • Un vacancier qui a un litige en cours peut-il faire une réservation ?

Toutes ces règles à respecter sont des invariants métier.

Coder les invariants

Certaines sont faciles à coder.

    // la date de départ doit être après la date d'arrivée
    if (from >= to) {
        return Maybe.error(WrongInterval(from, to))
    }

D'autres impliqueront d'aller interroger la base de données.

    // Un logement ne peut pas être loué 2 fois
    const bookings = await dependencies.booking.list({accomodationId});
    if (bookings.some(booking => overlapped(booking.interval, interval) ) ) {
        return Maybe.error(AccomodationNotAvailable(accomodationId))
    }

On sent qu'il sera difficile d'embarquer toutes ces règles dans le constructeur. Aucun choix évident de conception ne se dégage...

TDD

Alors voici une bonne façon de procéder : laissons-nous guider par les tests.

Dans un premier temps, on ne code AUCUN invariant. Et on fait passer notre test du scénario "tout va bien".

Puis pour chaque invariant, on ajoute un test. Et on modifie le strict minimun pour passer le test.

Cette méthode paraît complètement à l'opposé d'un processus d'ingénierie mûrement réfléchi dans lequel on code élégamment tous les cas possibles dont l'expert métier nous a parlé.

Et pourtant, vous serez étonnés du temps gagné et de la simplicité du code obtenu.

Alors faisons le choix le plus simple pour construire notre objet et faire passer notre test.

const booking = {
    tenantId:user.id, 
    accomodationId,
    hosts : {adults,children},
    interval : {from,to}
    };

Pas besoin d'une fonction pour construire une réservation directement issue des paramètres, sans vérifier aucun invariant (pour l'instant !).

Que reste-il sur notre liste ?

  • les dépendances
  • la classe App
  • le constructeur de l'objet Booking appelé dans la fonction book()

Tout y est !

Il est temps de passer notre test au vert.