# Normes

Note : Ces normes ne sont en aucun cas figĂ©es dans le marbre ; en tant que communautĂ©, nous souhaitons toujours les faire Ă©voluer et amĂ©liorer Mojaloop. Pour proposer une modification de ces normes ou suggĂ©rer des amĂ©liorations supplĂ©mentaires, n’hĂ©sitez pas Ă  contacter le canal Design Authority sur le Slack Mojaloop (#design-authority).

# Invariants Mojaloop

Mojaloop définit des invariants importants à comprendre et à respecter lorsque vous contribuez à la base de code.

Ces invariants dĂ©coulent des principes Level One (opens new window) et d’autres exigences mĂ©tier telles qu’arrĂȘtĂ©es par le conseil de gouvernance technique Mojaloop, le comitĂ© de contrĂŽle des changements d’API, la Design Authority et le conseil produit. Ils visent Ă  ce que la plateforme conserve des caractĂ©ristiques adaptĂ©es Ă  une exploitation de type infrastructure nationale.

Assurez-vous de connaĂźtre ces invariants avant de contribuer.

# Environnement d’exĂ©cution

Les normes d’exĂ©cution suivantes s’appliquent Ă  Mojaloop.

# Microservices et bibliothĂšques

  1. Javascript

    NodeJS est l’environnement d’exĂ©cution standard pour tous les services et composants Mojaloop pour l’exĂ©cution des fichiers de code source Javascript.

    Notre objectif est que tous les services NodeJS tournent sur la derniÚre version Active LTS (Long Time Support) de NodeJS, conformément au cycle de release NodeJS (opens new window).

  2. Conteneur (Docker) et systùme d’exploitation (OS)

    Les microservices Mojaloop sont construits Ă  partir de l’image de base node:<NODE_ACTIVE_LTS_VERSION>-alpine, oĂč NODE_ACTIVE_LTS_VERSION est la LTS NodeJS courante selon le cycle de release NodeJS (opens new window). Voir DockerHub (opens new window) pour la liste des images officielles Node Alpine.

    NOTE : pour NODE_ACTIVE_LTS_VERSION, utilisez la version sémantique complÚte <MAJOR>-<MINOR>-<PATCH>.

    node:16.15.0-alpine <-- correct

    lts-alpine3.16 <-- incorrect

    1. Exemple de Dockerfile Javascript standard :

      FROM node:<NODE_ACTIVE_LTS_VERSION>-alpine as builder
      WORKDIR /opt/app
      
      RUN apk --no-cache add git
      RUN apk add --no-cache -t build-dependencies make gcc g++ python3 libtool libressl-dev openssl-dev autoconf automake \
          && cd $(npm root -g)/npm \
          && npm config set unsafe-perm true \
          && npm install -g node-gyp
      
      COPY package*.json /opt/app/
      
      RUN npm ci --production
      
      FROM node:<NODE_ACTIVE_LTS_VERSION>-alpine
      WORKDIR /opt/app
      
      # Create empty log file & link stdout to the application log file
      RUN mkdir ./logs && touch ./logs/combined.log
      RUN ln -sf /dev/stdout ./logs/combined.log
      
      # Create a non-root user: ml-user
      RUN adduser -D ml-user
      USER ml-user
      
      # Copy builder artefact
      COPY --chown=ml-user --from=builder /opt/app .
      
      # Copy source files
      COPY src /opt/app/src
      
      # Copy default config
      COPY config /opt/app/config
      
      EXPOSE <PORT>
      CMD ["npm", "run", "start"]
      
    2. Exemple de Dockerfile Typescript standard :

      FROM node:<NODE_ACTIVE_LTS_VERSION>-alpine as builder
      USER root
      WORKDIR /opt/app
      
      RUN apk update \
          && apk add --no-cache -t build-dependencies git make gcc g++ python3 libtool autoconf automake openssh \
          && cd $(npm root -g)/npm \
          && npm config set unsafe-perm true \
          && npm install -g node-gyp
      
      COPY package.json package-lock.json* ./
      
      RUN npm ci
      
      FROM node:<NODE_ACTIVE_LTS_VERSION>-alpine
      WORKDIR /opt/app
      
      # Create empty log file & link stdout to the application log file
      RUN mkdir ./logs && touch ./logs/combined.log
      RUN ln -sf /dev/stdout ./logs/combined.log
      
      # Create a non-root user: ml-user
      RUN adduser -D ml-user
      USER ml-user
      
      # Copy builder artefact
      COPY --chown=ml-user --from=builder /opt/app ./
      
      COPY src /opt/app/src
      COPY config /opt/app/config
      
      # NPM script to build source (./src) to destination (./dist)
      RUN npm run build
      
      # Prune devDependencies
      RUN npm prune --production
      
      # Prune source files
      RUN rm -rf src
      
      EXPOSE <PORT>
      CMD ["npm", "run", "start"]
      

# Pipelines CI (Intégration Continue)

Les jobs CI Mojaloop s’exĂ©cutent sur la version Ubuntu LTS courante selon le cycle de release Ubuntu (opens new window).

# Kubernetes

Les charts Helm Mojaloop (mojaloop/helm (opens new window), mojaloop/charts (opens new window)) sont déployés et vérifiés sur la version Kubernetes LTS courante selon le cycle de release Kubernetes (opens new window).

# Guide de style

La communauté Mojaloop fournit des lignes directrices pour le style de code. Elles contribuent à maintenir une base de code de qualité, maintenable et cohérente.

Ces guides sont choisis car ils peuvent ĂȘtre appliquĂ©s et vĂ©rifiĂ©s avec des outils courants et peu de personnalisation. Bien que nous reconnaissions que les dĂ©veloppeurs ont des prĂ©fĂ©rences personnelles pouvant entrer en conflit avec ces rĂšgles, nous privilĂ©gions la cohĂ©rence au bike-shedding (opens new window).

L’objectif est de faciliter le flux de travail des dĂ©veloppeurs et de rĂ©duire les commits dont les changements sont motivĂ©s par le seul style plutĂŽt que par le fond. En rĂ©duisant le bruit dans les diffs, on facilite le travail des relecteurs.

# Style de code

# Conventions de nommage

Pour éviter toute confusion et garantir la cohérence des conventions entre les différents langages :

  • N’utilisez pas d’abrĂ©viations ou de contractions dans les identifiants. Ex. : SettlementWindow plutĂŽt que SetWin.
  • N’utilisez pas d’acronymes non universellement acceptĂ©s en informatique.
  • Lorsque c’est pertinent, utilisez des acronymes reconnus pour raccourcir. Ex. : UI pour User Interface.
  • Utilisez le Pascal case ou le camel case pour les noms de plus de deux caractĂšres, selon le contexte (classes vs variables). Ex. : SettlementWindow (classe) ou settlementWindow (variable).
  • Mettez en majuscules les acronymes de deux lettres isolĂ©s, ex. ID plutĂŽt que Id. Ex. : /transfer/ plutĂŽt que /transfer/ pour un paramĂštre d’URI.
  • Évitez les abrĂ©viations dans les identifiants ou paramĂštres. Si vous devez en utiliser, camel case pour les abrĂ©viations de plus de deux caractĂšres, mĂȘme si cela diverge de l’abrĂ©viation usuelle.
  • Utilisez le SCREAMING_SNAKE_CASE pour les Ă©numĂ©rations. Ex. : RECORD_FUNDS_OUT_PREPARE_RESERVE.

Réf. : Microsoft - Design Guidelines for Class Library Developers (opens new window)

# Javascript

Mojaloop suit le style Javascript défini par StandardJS (opens new window). RÚgles complÚtes : Standard Rules (opens new window). Extraits :

  • 2 espaces pour l’indentation
function helloWorld (name) {
  console.log('hi', name)
}
  • Guillemets simples pour les chaĂźnes sauf pour Ă©viter d’échapper.
console.log('hello there')    // ✓ ok
console.log("hello there")    // ✗ avoid
console.log(`hello there`)    // ✗ avoid
  • Pas de point-virgules. (voir : 1, 2, 3)
window.alert('hi')   // ✓ ok
window.alert('hi');  // ✗ avoid

# Typescript

Note : Standard et Typescript

À mesure que nous introduisons davantage de TypeScript dans la base de code, Standard devient moins adaptĂ© et peut mĂȘme nuire Ă  notre flux de travail si l’on tente d’appliquer Standard au Javascript compilĂ© depuis TypeScript. Il faudra Ă©valuer d’autres options pour Typescript, par ex. Prettier + ESLint.

Voir le dépÎt template-typescript-public (opens new window) pour la configuration Typescript de référence.

# YAML

Les désérialiseurs YAML peuvent varier ; rÚgles suivantes :

Crédit : exemples issus du guide de style Flathub (opens new window)

  • Indentation de 2 espaces
  • Toujours indenter les Ă©lĂ©ments enfants
# BON :
modules:
  - name: foo
    sources:
      - type: bar

# MAUVAIS :
modules:
- name: foo
  sources:
  - type: bar
  • N’alignez pas les valeurs sur des colonnes
# MAUVAIS :
id:           org.example.Foo
modules:
  - name:     foo
    sources:
      - type: git

# sh + bash

  • Le shebang doit respecter l’environnement local de l’utilisateur :
#!/usr/bin/env bash

Le script utilisera le bash dĂ©fini dans l’environnement plutĂŽt qu’un chemin codĂ© en dur.

  • Pour rĂ©fĂ©rencer d’autres fichiers, Ă©vitez les chemins relatifs simples :

En effet, si quelqu'un exĂ©cute le script depuis un rĂ©pertoire diffĂ©rent de celui oĂč il se trouve, le script risque fort de ne pas fonctionner.

# MAUVAIS :
cat ../Dockerfile | wc -l

# BON :
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
cat ${DIR}/../Dockerfile | wc -l

Autres bonnes pratiques bash : Best Practices for Writing Shell Scripts (opens new window)

# Documentation

  • RĂ©diger la documentation en Markdown.
  • Les schĂ©mas dessinĂ©s Ă  la main doivent utiliser un format SVG Ă©ditable (ex. : architecture / composants / blocs / diagrammes de transition d'Ă©tat), exportĂ©s depuis diagrams.net (opens new window)

    NOTE : intĂ©grez le diagramme Ă©ditable lors de l’export SVG depuis diagrams.net (opens new window) !

  • Diagrammes de sĂ©quence : PlantUML
  • Les documents de discussion doivent ĂȘtre placĂ©s dans /community/archive/discussion-docs.
  • Il est dĂ©conseillĂ© d’utiliser Google Docs et autres outils propriĂ©taires pour la collaboration Ă  l’échelle de la communautĂ©.

# Structure de répertoires

Outre le style de code, la communautĂ© recommande la structure suivante afin que les dĂ©veloppeurs puissent passer facilement d’un projet Ă  l’autre, et que nos outils et configurations (.circleci/config.yml, Dockerfiles, etc.) puissent ĂȘtre portĂ©s d’un projet Ă  l’autre avec des modifications mineures.

Structure attendue :

├── README.md          # Infos gĂ©nĂ©rales : prĂ©requis, tests, etc.
├── LICENSE.md         # Descripteur de licence Mojaloop standard.
├── package.json       # Descripteur npm du projet.
├── package-lock.json  # Arbre de dĂ©pendances exact dans le temps.
├── nvmrc.json         # NVMRC : runtime NodeJS (de prĂ©fĂ©rence Active LTS).
├── .ncurc.yaml        # Ignore pour le script dep:check (npm-check-updates).
├── Dockerfile         # Optionnel
├── docker-compose.yml # Optionnel (dĂ©pendances backend, etc.)
├── .npmignore         # Optionnel — publication des bibliothùques npm.
├── .gitignore         # Fichier ignore GitHub.
├── src                # Sources du projet.
│   ├── index.<js/ts>    # Point d’entrĂ©e principal.
│   ├── <filename>.<js/ts> # Convention des fichiers sources.
│   └── ...
├── dist               # Javascript compilĂ© (voir tsconfig).
├── test               # Tests, au minimum :
│   ├── unit           # Tests unitaires, structure alignĂ©e sur `./src`.
│   │   ├── <filename>.test.<js/ts>
│   │   └── ...
│   ├── integration    # Tests d’intĂ©gration.
│   ├── functional     # Tests fonctionnels.
│   └── util           # Scripts et helpers de test.
└── config
    └── default.json   # Configuration par dĂ©faut.

# Fichiers de configuration

Ces fichiers renforcent les styles de code ci-dessus :

# EditorConfig

EditorConfig est pris en charge nativement dans de nombreux IDE. Voir le guide EditorConfig (opens new window).

.editorconfig

root = true

[*]
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
charset = utf-8

[{*.js,*.ts,package.json,*.yml,*.cjson}]
indent_style = space
indent_size = 2

[*.md]
trim_trailing_whitespace = false

# NYC (couverture de code)

.nycrc.yml

temp-directory: "./.nyc_output"
check-coverage: true
per-file: true
lines: 90
statements: 90
functions: 90
branches: 90
all: true
include: [
  "src/**/*.js"
]
reporter: [
  "lcov",
  "text-summary"
]
exclude: [
  "**/node_modules/**",
  '**/migrations/**'
]

# Typescript

.tsconfig.json

{
  "include": [
    "src"
  ],
  "exclude": [
    "node_modules",
    "**/*.spec.ts",
    "test",
    "lib",
    "coverage"
  ],
  "compilerOptions": {
    "target": "es2018",
    "module": "commonjs",
    "lib": [
      "esnext"
    ],
    "importHelpers": true,
    "declaration": true,
    "sourceMap": true,
    "rootDir": "./src",
    "outDir": "./dist",
    "strict": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true,
    "strictPropertyInitialization": true,
    "noImplicitThis": true,
    "alwaysStrict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "moduleResolution": "node",
    "baseUrl": "./",
    "paths": {
      "*": [
        "src/*",
        "node_modules/*"
      ]
    },
    "esModuleInterop": true
  }
}

.eslintrc.js

module.exports = {
  parser: '@typescript-eslint/parser',  // Specifies the ESLint parser
  extends: [
    'plugin:@typescript-eslint/recommended',  // Uses the recommended rules from the @typescript-eslint/eslint-plugin
    'prettier/@typescript-eslint', // Uses eslint-config-prettier to disable ESLint rules from @typescript-eslint/eslint-plugin that would conflict with prettier
    'plugin:prettier/recommended', // Enables eslint-plugin-prettier and displays prettier errors as ESLint errors. Make sure this is always the last configuration in the extends array.
    // Enforces ES6+ import/export syntax
    'plugin:import/errors',
    'plugin:import/warnings',
    'plugin:import/typescript',
  ],
  parserOptions: {
    ecmaVersion: 2018,  // Allows for the parsing of modern ECMAScript features
    sourceType: 'module',  // Allows for the use of imports
  },
  rules: {
    '@typescript-eslint/no-explicit-any': 'off',
    '@typescript-eslint/no-var-requires': 'off'
  },
  overrides: [
    {
      // Disable some rules that we abuse in unit tests.
      files: ['test/**/*.ts'],
      rules: {
        '@typescript-eslint/explicit-function-return-type': 'off',
      },
    },
  ],
};

Configuration Typescript détaillée (package.json, jest.config.js, etc.) : Typescript Template Project (opens new window).

# Gestion des dépendances

# Mises à jour des dépendances

Il est important d’utiliser des dĂ©pendances Ă  jour afin d’attĂ©nuer les risques de sĂ©curitĂ©.

# NodeJS

Installez npm-check-updates (opens new window) :

npm install -D npm-check-updates

Ajoutez dans package.json :

"scripts": {
    "dep:check": "npx ncu -e 2",
    "dep:update": "npx ncu -u"
}

Pour vérifier les mises à jour :

npm run dep:check

Pour installer les derniĂšres versions :

npm run dep:update && npm i

Si une dĂ©pendance ne peut pas ĂȘtre mise Ă  jour pour une raison valide, ajoutez .ncurc.yaml Ă  la racine avec la dĂ©pendance dans reject et un comment :

## TODO : raison pour chaque rejet et comment le traiter (story, etc.).
reject: [
  # TODO: <Insert detailed information as to why this dependency should be ignored.>
  "<DEPENDENCY_TO_IGNORE>",
]

Moyens pour maintenir les dépendances à jour :

# Hook Git pre-commit

Validation locale Ă  chaque commit Git.

Ajoutez dep:check comme hook pre-commit avec Husky (opens new window) :

npx husky add .husky/pre-commit "npm run dep:check"

On peut contourner avec git commit -nm <message>. Un job CI test-dependencies (section suivante) est donc nécessaire.

# Validations CI automatisées

ContrÎles lors des revues et releases ; évite le contournement du hook.

Les configs CI (.circleci/config.yml) doivent inclure un job test-dependencies (npm run dep:check) pour les PR, fusions vers la branche principale et releases étiquetées.

# Audit des dépendances

# NodeJS

Installez audit-ci (opens new window) :

npm install -D audit-ci

Ajoutez dans package.json :

"scripts": {
    "audit:check": "npx audit-ci --config ./audit-ci.jsonc"
}

Pour vérifier les mises à jour :

npm run audit:check

Correctifs connus via npm audit (opens new window) :

npm audit fix --package-lock-only

NOTES

  1. Commitez les correctifs appliqués au package-lock.json.
  2. Relancez les tests : un changement de version peut introduire des ruptures.

Sans correctif, ajoutez audit-ci.jsonc à la racine avec l’ID d’avis dans allowlist et un commentaire :

{
  "$schema": "https://github.com/IBM/audit-ci/raw/main/docs/schema.json",
  // audit-ci supports reading JSON, JSONC, and JSON5 config files.
  // Only use one of ["low": true, "moderate": true, "high": true, "critical": true]
  "moderate": true,
  "allowlist": [ // NOTE: Please add as much information as possible to any items added to the allowList
    // Currently no fixes available for the following advisory ID
    "<VULNERABILITY_ADVISORY_ID>"
  ]
}
# Hook Git pre-commit

Exécute les contrÎles de vulnérabilités localement à chaque commit.

Ajoutez audit:check avec Husky (opens new window) :

npx husky add .husky/pre-commit "npm run audit:check"

Contournement possible avec git commit -nm. Un job CI vulnerability-check (section suivante) est requis.

# Validations CI automatisées

Audits lors des revues et releases ; évite le contournement du hook.

Les configs CI (.circleci/config.yml) doivent inclure un job vulnerability-check (npm run audit:check) pour les PR, fusions vers la branche principale et releases étiquetées.

# Lignes directrices de conception et d’implĂ©mentation

Recommandations pour le code au sein de la communauté Mojaloop (ou le code qui y sera intégré). Si vous souhaitez donner du code à la communauté, suivez ces lignes directrices autant que possible pour la cohérence et la maintenabilité. Les contributions alignées seront intégrées plus facilement.

Voir la FAQ ci-dessous.

# Outils et frameworks

La communauté OSS Mojaloop privilégie les outils et frameworks suivants :

En utilisant ces outils et frameworks, nous maintenons un haut niveau de cohĂ©rence et de maintenabilitĂ© sur l’ensemble de la base de code, ce qui permet Ă  nos dĂ©veloppeurs de rester productifs et sereins. Nous n’imposons pas leur usage aux dĂ©pĂŽts fournis en contribution, mais d’autres choix peuvent alourdir la maintenance pour la communautĂ©.

# Adopter des contributions open source dans Mojaloop

Lignes directrices pour l’adoption d’une contribution dans les dĂ©pĂŽts open source Mojaloop. L’adoption est le processus par lequel la communautĂ© accompagne un contributeur pour aligner sa contribution sur nos normes afin qu’elle rejoigne la base de code OSS Mojaloop.

Note : les contributions sont Ă©valuĂ©es au cas par cas. Celles qui ne respectent pas ces lignes directrices passent par la phase d’incubation ci-dessous. D’autres Ă©carts (p. ex. choix de framework) peuvent figurer sur une feuille de route pour standardisation ultĂ©rieure.

# Étape 0 : prĂ©requis

Avant qu’une contribution soit considĂ©rĂ©e pour adoption :

  1. Elle doit ĂȘtre alignĂ©e avec les principes du Level One Project (opens new window).
  2. Elle doit respecter le guide de style et les lignes directrices de conception et d’implĂ©mentation ci-dessus.
  3. Elle doit inclure de la documentation de dĂ©marrage : plus il y en a, mieux c’est.
  4. Elle doit inclure des tests avec une bonne couverture. Au minimum des tests unitaires ; une suite unitaire, d’intĂ©gration et fonctionnelle est prĂ©fĂ©rĂ©e. Voir le guide des contributeurs.

# Étape 1 : incubation

  1. CrĂ©er un dĂ©pĂŽt privĂ© dans l’organisation GitHub Mojaloop pour le code adoptĂ©.
  2. Faire examiner par une sous-Ă©quipe de la DA pour vĂ©rifier la portabilitĂ© (vers l’OSS), l’alignement avec les principes L1P, etc., et la conformitĂ© de la conception aux normes.
  3. Vérifier les licences de la contribution et des nouvelles dépendances ; ajouter la licence Mojaloop standard avec attribution aux donateur(s)/contributeur(s).
  4. Évaluer l’état du code : documentation, tests, qualitĂ© ; combler les lacunes.
  5. Évaluer l’impact sur les performances.
  6. Créer des actions (stories) pour renommer, retirer ou anonymiser tout élément non générique.
  7. Examiner et discuter les choix de frameworks et d’outils.
  • En cas de dĂ©cision de changement, les ajouter Ă  la feuille de route.

# Étape 2 : adoption publique

  1. Rendre le projet public sur GitHub Mojaloop.
  2. Annoncer sur le canal Slack #announcements (opens new window).
  3. Activer les pipelines CI/CD et publier les artefacts pertinents (images Docker, modules npm, etc.).
  4. Examiner et recommander un module ou une formation pour le programme de formation Mojaloop si pertinent.

# Versionnement

Voir versionnement pour Mojaloop.

# Créer de nouvelles fonctionnalités

Processus pour les fonctionnalités et branches dans Mojaloop.

# Processus de pull request

Pour les changements importants, demandez conseil sur Slack (opens new window). Les PR doivent décrire le changement et sa motivation. Vous pouvez utiliser les brouillons de PR GitHub pour recueillir commentaires et revue.

Les PR qui violent les principes Level One (opens new window) seront refusées.

# Code de conduite

Nous appliquons le code de conduite de la Fondation Mojaloop (opens new window).

# Licence

Voir la politique License (opens new window).

# FAQ

1. Je veux contribuer du code qui ne suit pas le style ou les outils recommandés dans ce guide.

Les contributions sont acceptĂ©es au cas par cas. Si la contribution n’est pas prĂȘte pour une adoption complĂšte, nous pouvons passer par la phase d’incubation : refactoring avec notre aide pour aligner code et documentation.

2. Ces normes sont dépassées et un outil (ou framework, méthode, langage) plus récent résoudrait le problÚme x. Comment mettre à jour les normes ?

Écrire du code de qualitĂ© est une cible en constante Ă©volution, et nous cherchons toujours activement de nouveaux outils susceptibles d’amĂ©liorer la base de code OSS Mojaloop. N’hĂ©sitez donc pas Ă  nous en parler sur le canal Slack design authority (#design-authority) si vous avez une recommandation.