Transparence (1/1) - Organiser ses calculs 
Tags: ddd, architecture
Published: 2020-11-07 14:20:43

Etre transparent sur son travail ça n'est pas toujours simple et possible. Pour des questions évidentes de confidentialité.

On peut néanmoins en extraire des morceaux à partager.

Récemment j'ai eu à travailler sur un projet dans le domaine de l'agriculture, où le besoin était de fournir une interface pour saisir des données et afficher des calculs. 

Le soucis que l’on a rencontré, c'est que les calculs il y en avait beaucoup, pas très simple et pour ceux qui n'y connaissent rien, assez compliqué à vérifier.

> En effet comment savoir que tel résultat est juste si on a aucune idée de ce qu'on doit obtenir ? Surtout où les données sont des pourcentages, des nombres à virgules...

## Demander de l'aide à l'expert du domaine

Ce que j'ai fais en premier lieu, c'est demander à "l'expert" du domaine chez le client de nous remplir un fichier yaml sous un format précis.

Je lui demandais de me donner le nom du calcul, les valeurs en entrées et le résultat attendu.

```yaml
calculs:
  calcul_A:
    given:
      param_1: 2.2
      param_2: 0.01
      param_3: 32
    expected: 8
```

Ainsi on a pu tester automatiquement énormément de calculs, et s'il nous soumettait un nouveau fichier yaml, on pouvait le valider rapidement.

## Implémenter les calculs

Je me suis demandé comment organiser le code pour tous ses calculs.

Mon architecture est basée sur le modèle hexagonale, et sur une philosophie DDD. J'ai donc un Dossier AgricultureContext (j'invente un nom pour l'article), et je ne voulais pas que les calculs fassent partie de ce contexte.

Pourquoi ? et bien les calculs, c'est toute une connaissance à part entière à gérer, qui peut être indépendante du reste du projet. Je me suis dis, peut-être que je devrai penser comme si les calculs venaient d'ailleurs, comme une API externe.

Du coup j'ai pensé, pourquoi ne pas faire un autre contexte dédié aux calculs. Au final tout ce qu'on veut c'est que pour des données en entrées, on souhaite obtenir un résultat. 

Dans ce contexte, on retrouvera des éléments commun en terme de nommage _(en DDD on parle tous le même language, mais selon les contextes on en fait pas la même chose)_, mais le but de ce contexte, c'est d'effectuer des calculs et de savoir comment les faire. 

Je suis un fervent défenseur de "la responsabilité unique". Une classe ne doit faire qu'une seule chose et bien. On peut étendre ce concept à un niveau plus général, c'est pourquoi j'ai voulu sortir les calculs du contexte Agriculture. 

Ainsi on peut faire évoluer le code dans le CalculContext sans aucun impact sur le reste. On pourrait même un jour faire un autre projet séparé, reprenant le code du CalculContext, et pourquoi pas à la place de partager une interface dans un monoprojet, de passer par une API.

En tout cas c'est la voie que j'ai choisi. Avec le recul, est-ce que j'aurai pu mettre tous les calculs dans le même contexte ? J'y ai pensé, mais à faire ça, je sais que j'aurai eu un code très couplé avec le domaine et moins simple à tester.

Donc pour faire le lien entre les deux contextes, j'autorise un service à être appelé dans le contexte Agriculture. Pour cela j'utilise le concept "d'anti-corruption layer". C'est à dire transformer le model d'un domaine vers un autre. Par exemple traduire une classe vers une autre, du json vers une classe, ou une classe vers du xml etc. Le but étant de ne pas manipuler les données d'un autre domaine sans l'avoir transformé pour son propre domaine auparavant. 

> Si je n'avais pas fais ça, je me serai retrouvé à manipuler des classes d'un autre domaine (un autre namespace), ce qui viendrait à coupler notre code très fortement et casser les barrières de nos contextes. Ne faites pas cela.

Concernant les calculs en eux mêmes, j’ai choisi de créer une classe par calcul, avec en paramètres un tableau de données, avec validation des noms et du type attendu.

Et voilà, on arrive à la fin de cette petite transparence de code. 

J'essaierai d'en écrire d'autre, j'ai plein de sujet en tête.