Organisation et architecture des Programmes de Services

 Organisation et architecture des SRVPGM

(Ce document est aussi un hommage à l’immense Stevie Ray Vaughan )

L'approche moderne recommandée

L'industrie IBM i tend vers un hybride pragmatique :

Architecture modulaire par domaine :

CUSTOMER_SRVPGM

  ├─ CUSTVAL (*MODULE)    // Validation

  ├─ CUSTCRUD (*MODULE)   // CRUD operations

  └─ CUSTUTIL (*MODULE)   // Utilitaires


INVOICE_SRVPGM

  ├─ INVCALC (*MODULE)

  └─ INVFMT (*MODULE)

Principes clés :

  1. *Un SRVPGM par domaine métier

  2. *Des MODULE pour la granularité 

  3. *Un fichier de prototypes par SRVPGM 

  4. Binding directory pour simplifier la liaison

Exemple concret :

// QRPGLESRC/CUSTOMER_H

// Fichier unique de prototypes pour CUSTOMER_SRVPGM

dcl-pr validerClient ind;

  clientId packed(9:0) const;

end-pr;

dcl-pr creerClient packed(9:0);

  nom varchar(50) const;

  email varchar(100) const;

end-pr;

dcl-pr supprimerClient ind;

  clientId packed(9:0) const;

end-pr;



Utilisation dans un programme :

**free

ctl-opt bnddir('MYAPP');  // Contient CUSTOMER_SRVPGM


/include qrpglesrc,customer_h


dcl-s nouveauId packed(9:0);


nouveauId = creerClient('Dupont':'dupont@mail.com');

if validerClient(nouveauId);

  // ...

endif;


Le bon ratio de fonction dans les fonctions/modules : par responsabilité cohérente

*Un MODULE = un ensemble de procédures partageant une responsabilité commune

Pas une procédure = un module (trop granulaire), ni tout dans un module (pas assez granulaire).

Exemples concrets

❌ Trop granulaire (à éviter)

CUSTOMER_SRVPGM

  ├─ CUSTVAL1 (*MODULE)  → juste validerEmail()

  ├─ CUSTVAL2 (*MODULE)  → juste validerTelephone()

  ├─ CUSTVAL3 (*MODULE)  → juste validerCodePostal()

  └─ CUSTVAL4 (*MODULE)  → juste validerSiret()

Problème : Complexité de build, trop de fichiers, overhead de linking

❌ Pas assez granulaire

CUSTOMER_SRVPGM (source unique compilé directement)

  → validerEmail()

  → validerTelephone()

  → creerClient()

  → modifierClient()

  → supprimerClient()

  → calculerChiffreAffaires()

  → exporterVersCompta()

  → envoyerEmailBienvenue()

Problème : Modification d'une ligne = recompilation complète

✅ Juste équilibre (recommandé)

CUSTOMER_SRVPGM

  ├─ CUSTVAL (*MODULE)     → Toutes les validations

  │   ├─ validerEmail()

  │   ├─ validerTelephone()

  │   ├─ validerCodePostal()

  │   └─ validerSiret()

  │

  ├─ CUSTCRUD (*MODULE)    → Opérations CRUD

  │   ├─ creerClient()

  │   ├─ lireClient()

  │   ├─ modifierClient()

  │   └─ supprimerClient()

  │

  ├─ CUSTBUS (*MODULE)     → Logique métier

  │   ├─ calculerChiffreAffaires()

  │   ├─ calculerStatutFidelite()

  │   └─ verifierLimiteCredit()

  │

  └─ CUSTINT (*MODULE)     → Intégrations externes

      ├─ exporterVersCompta()

      ├─ synchroniserCRM()

      └─ envoyerEmailBienvenue()

Les critères de regroupement

1. Responsabilité fonctionnelle (le plus important)

Regroupe ce qui change pour les mêmes raisons :

  • Validations ensemble

  • CRUD ensemble

  • Calculs métier ensemble

  • Intégrations externes ensemble

2. Fréquence de modification

  • Code stable (validations basiques) → peut être plus gros

  • Code volatile (règles métier) → plus petit et isolé

3. Dépendances

  • Procédures qui s'appellent entre elles → même module

  • Procédures indépendantes → modules séparés si logique

4. Taille raisonnable

Règle empirique :

  • 3 à 10 procédures par module en moyenne

  • 500 à 2000 lignes de code par module

  • Si > 2500 lignes → probablement à découper

  • Si < 200 lignes avec 1 seule proc → probablement à fusionner

Exemple détaillé : module CUSTVAL

**free

// QRPGLESRC/CUSTVAL

// Module de validation client


ctl-opt nomain;  // C'est un module, pas un programme

/include qrpglesrc,customer_h


//--------------------------------------------------

// Valide une adresse email

//--------------------------------------------------

dcl-proc validerEmail export;

  dcl-pi *n ind;

    email varchar(100) const;

  end-pi;

 

  dcl-s regex varchar(200);

 

  if %len(email) = 0;

    return *off;

  endif;

 

  // Logique validation regex

  // ...

  return *on;

end-proc;


//--------------------------------------------------

// Valide un numéro de téléphone français

//--------------------------------------------------

dcl-proc validerTelephone export;

  dcl-pi *n ind;

    tel varchar(20) const;

  end-pi;


  // Logique validation téléphone

  // ... 

  return *on;

end-proc;


//--------------------------------------------------
// Valide un code postal français
//--------------------------------------------------

dcl-proc validerCodePostal export;

  dcl-pi *n ind;
    cp char(5) const;
  end-pi;

  // Logique validation CP

  // ...

  return *on;

end-proc;

//--------------------------------------------------
// Valide un numéro SIRET
//--------------------------------------------------

dcl-proc validerSiret export;

  dcl-pi *n ind;

    siret char(14) const;

  end-pi;

  // Logique validation SIRET (algo Luhn)

  // ...

  return *on;

end-proc;


//--------------------------------------------------
// Procédure interne (non exportée)
// Utilisée par les autres validations
//--------------------------------------------------

dcl-proc verifierLuhn;

  dcl-pi *n ind;

    numero char(50) const;

  end-pi;

  // Algorithme de Luhn

  // ...

  return *on;

end-proc;


Points clés :

  • ctl-opt nomain; → c'est un module, pas un programme standalone

  • export sur les procédures publiques

  • Pas de export sur verifierLuhn() → privée au module

  • Toutes les validations regroupées (cohésion)

Règle d'or pragmatique

Se poser ces questions :

  1. "Si je modifie cette procédure, est-ce que les autres du module risquent de changer aussi ?"

    • Oui → bon regroupement

    • Non → peut-être à séparer

  2. "Est-ce que ces procédures partagent des utilitaires internes ?"

    • Oui → même module

    • Non → modules séparés possibles

  3. "Est-ce que je peux nommer le module avec UN concept clair ?"

    • Oui ("Validation", "CRUD", "Export") → bon module

    • Non ("Divers", "Utils") → probablement mal découpé

Le problème des dépendances circulaires


SRVPGM_A → utilise → SRVPGM_B

    ↑                      ↓

    └──────────────────────┘

         utilise

Pourquoi c'est aberrant :

  1. Impossible à compiler proprement (ordre de build ?)

  2. Couplage fort → maintenance cauchemardesque

  3. Réutilisation impossible → tu ne peux pas prendre A sans B ni B sans A

  4. Signature instable → changement dans A impacte B qui impacte A...

  5. Testabilité nulle → impossible de tester unitairement

Comment on arrive à cette situation

Scénario typique :

// CLIENT_SRVPGM
dcl-proc creerClient;
  // …
  creerAdresseDefaut(clientId);  // Appel vers ADRESSE_SRVPGM
  // …
end-proc;

// ADRESSE_SRVPGM 
dcl-proc creerAdresseDefaut;
  // …
  if validerClient(clientId);  // Oups ! Appel vers CLIENT_SRVPGM
    // …
  endif;
end-proc;
```

Pourquoi ça arrive :

- Mauvaise séparation des responsabilités

- Logique métier éparpillée

- Évolution organique sans refactoring

- Méconnaissance des principes de découplage

Les solutions

✅ Solution 1 : Extraction de dépendances communes


Principe : Créer un *SRVPGM de niveau inférieur contenant les fonctions partagées

```


Avant (circulaire) :

CLIENT_SRVPGM ←→ ADRESSE_SRVPGM


Après (hiérarchique) :

     CLIENT_SRVPGM     ADRESSE_SRVPGM

            ↓               ↓

         VALIDATION_SRVPGM (commun)

Exemple concret :

// VALIDATION_SRVPGM (couche basse)
dcl-proc validerClient export;
  dcl-pi *n ind;
    clientId packed(9:0) const;
  end-pi;
  // Validation pure, sans dépendance
  return clientId > 0;
end-proc;

dcl-proc validerAdresse export;
  dcl-pi *n ind;
    adresseId packed(9:0) const;
  end-pi;

  return adresseId > 0;
end-proc;

// CLIENT_SRVPGM (couche haute)
// Utilise : VALIDATION_SRVPGM + ADRESSE_SRVPGM
dcl-proc creerClient export;
  // …
  if validerClient(clientId);
    adresseId = creerAdresseDefaut(clientId);
  endif;
  // …
end-proc;

// ADRESSE_SRVPGM (couche haute)
// Utilise : VALIDATION_SRVPGM uniquement
dcl-proc creerAdresseDefaut export;
  dcl-pi *n packed(9:0);
    clientId packed(9:0) const;
  end-pi;

// N'appelle PLUS validerClient du CLIENT_SRVPGM
// Mais validerClient du VALIDATION_SRVPGM
  if not validerClient(clientId);
    return 0;
  endif;
// …
  return nouvelleAdresseId;
end-proc;

✅ Solution 2 : Inversion de dépendance (Dependency Injection)

Principe : Passer la fonction de validation en paramètre

// ADRESSE_SRVPGM

dcl-proc creerAdresseDefaut export;

  dcl-pi *n packed(9:0);

    clientId packed(9:0) const;

    validateurClient pointer(*proc) const;  // Fonction passée en param

  end-pi;

 

  dcl-pr validateur ind extproc(validateurClient);

    id packed(9:0) const;

  end-pr;

 

  if not validateur(clientId);

    return 0;

  endif;

 

  // ...

end-proc;


// CLIENT_SRVPGM - Appelant

adresseId = creerAdresseDefaut(clientId : %paddr(validerClient));

```



Avantage : ADRESSE_SRVPGM ne dépend plus de CLIENT_SRVPGM


✅ Solution 3 : Architecture en couches strictes


Règle d'or :Une dépendance ne va QUE vers le bas, jamais vers le haut ou latéralement.


┌─────────────────────────────────────┐

│  Couche Application (programmes)    │

├─────────────────────────────────────┤

│  Couche Métier (SRVPGM métier)      │

│  CLIENT_SRVPGM  ADRESSE_SRVPGM      │

├─────────────────────────────────────┤

│  Couche Services (SRVPGM utils)     │

│  VALIDATION_SRVPGM  EMAIL_SRVPGM    │

├─────────────────────────────────────┤

│  Couche Infrastructure (DB, API)    │

│  DBUTIL_SRVPGM  HTTPCLIENT_SRVPGM   │

└─────────────────────────────────────┘



Règle : On ne dépend QUE de la couche du dessous


Exemple d'organisation :


```

// Couche Infrastructure (niveau 0 - aucune dépendance métier)

DBUTIL_SRVPGM

  → executeSQL()

  → prepareStatement()


HTTPCLIENT_SRVPGM

  → httpGet()

  → httpPost()


// Couche Services (niveau 1 - dépend de Infrastructure)

VALIDATION_SRVPGM  (utilise: rien ou DBUTIL)

  → validerEmail()

  → validerSiret()


EMAIL_SRVPGM  (utilise: HTTPCLIENT_SRVPGM)

  → envoyerEmail()

  → verifierFormat()


// Couche Métier (niveau 2 - dépend de Services)

CLIENT_SRVPGM  (utilise: VALIDATION + EMAIL + DBUTIL)

  → creerClient()

  → validerClient()


ADRESSE_SRVPGM  (utilise: VALIDATION + DBUTIL)

  → creerAdresse()

  → normaliserAdresse()


FACTURE_SRVPGM  (utilise: CLIENT + ADRESSE + EMAIL)

  → creerFacture()

  → envoyerFacture()

```



✅ Solution 4 : Refactoring radical si nécessaire


Parfois, la circularité révèle un découpage conceptuel erroné.


Exemple :


```

Problème : CLIENT et ADRESSE trop couplés


Solution : Créer CLIENTCOMPLET_SRVPGM qui englobe les deux

```

🔍 Détection : Comment détecter et prévenir


Commande IBM i pour voir les dépendances :


```

DSPSRVPGM SRVPGM(MALIB/CLIENT_SRVPGM)

Option 11 = Afficher les dépendances

Script CL pour audit :

PGM

  DCL VAR(&LIB) TYPE(*CHAR) LEN(10) VALUE('MALIB')

 

  /* Liste tous les SRVPGM et leurs dépendances */

  DSPOBJD OBJ(&LIB/*ALL) OBJTYPE(*SRVPGM) +
          OUTPUT(*OUTFILE) +
          OUTFILE(QTEMP/SRVLIST)

  /* Analyse des dépendances circulaires */
 /* (nécessite un programme pour parser) */

ENDPGM



🛡️ Prévention

1. Règle de design :

"Si A utilise B, alors B ne doit JAMAIS utiliser A, ni directement ni indirectement"

2. Checklist avant création d'un SRVPGM :

  • Quelle est sa responsabilité unique ?

  • De quels *SRVPGM dépend-il ?

  • Ces dépendances sont-elles "vers le bas" ?

  • Y a-t-il un risque de retour circulaire ?

3. Revue de code systématique : Lors de l'ajout d'un /include dans un *SRVPGM, vérifier qu'on ne crée pas de cycle.

4. Documentation des dépendances :

**free

//====================================================
// CLIENT_SRVPGM
//
// Dépendances:
//   - VALIDATION_SRVPGM (services)
//   - EMAIL_SRVPGM (services)
//   - DBUTIL_SRVPGM (infrastructure)
//
// Utilisé par:
//   - FACTURE_SRVPGM
//   - COMMANDE_SRVPGM
//===================================================


Solution 1 : Script SQL complet

-- =========================================================

-- DÉTECTION DES DÉPENDANCES CIRCULAIRES ENTRE *SRVPGM

-- =========================================================


-- 1. Créer une table temporaire des dépendances directes

CREATE OR REPLACE TABLE QTEMP.SRVPGM_DEPS AS (

  SELECT

    TRIM(A.SYSTEM_OBJECT_SCHEMA) AS LIB_SOURCE,

    TRIM(A.SYSTEM_OBJECT_NAME) AS SRVPGM_SOURCE,

    TRIM(B.BOUND_OBJECT_LIBRARY) AS LIB_CIBLE,

    TRIM(B.BOUND_OBJECT) AS SRVPGM_CIBLE,

    B.BOUND_OBJECT_TYPE AS TYPE_CIBLE

  FROM QSYS2.PROGRAM_INFO A

  INNER JOIN TABLE(QSYS2.BOUND_SRVPGM_INFO(

    SERVICE_PROGRAM_LIBRARY => A.SYSTEM_OBJECT_SCHEMA,

    SERVICE_PROGRAM => A.SYSTEM_OBJECT_NAME

  )) B

  WHERE A.OBJECT_TYPE = '*SRVPGM'

    AND A.SYSTEM_OBJECT_SCHEMA = 'VOTRE_LIB'  -- ← ADAPTER ICI

    AND B.BOUND_OBJECT_TYPE = '*SRVPGM'

) WITH DATA;


-- 2. Détecter les cycles directs (A → B et B → A)

SELECT

  'CYCLE DIRECT' AS TYPE_CYCLE,

  D1.LIB_SOURCE || '/' || D1.SRVPGM_SOURCE AS SRVPGM_A,

  D1.LIB_CIBLE || '/' || D1.SRVPGM_CIBLE AS SRVPGM_B,

  '←→' AS RELATION

FROM QTEMP.SRVPGM_DEPS D1

INNER JOIN QTEMP.SRVPGM_DEPS D2

  ON D1.LIB_SOURCE = D2.LIB_CIBLE

  AND D1.SRVPGM_SOURCE = D2.SRVPGM_CIBLE

  AND D1.LIB_CIBLE = D2.LIB_SOURCE

  AND D1.SRVPGM_CIBLE = D2.SRVPGM_SOURCE

WHERE D1.SRVPGM_SOURCE < D2.SRVPGM_SOURCE  -- Éviter les doublons


UNION ALL


-- 3. Détecter les cycles indirects (A → B → C → A)

SELECT

  'CYCLE INDIRECT' AS TYPE_CYCLE,

  D1.LIB_SOURCE || '/' || D1.SRVPGM_SOURCE AS SRVPGM_A,

  D3.LIB_CIBLE || '/' || D3.SRVPGM_CIBLE AS SRVPGM_C,

  D1.SRVPGM_SOURCE || ' → ' || D2.SRVPGM_CIBLE || ' → ' || D3.SRVPGM_CIBLE || ' → ' || D1.SRVPGM_SOURCE AS RELATION

FROM QTEMP.SRVPGM_DEPS D1

INNER JOIN QTEMP.SRVPGM_DEPS D2

  ON D1.LIB_CIBLE = D2.LIB_SOURCE

  AND D1.SRVPGM_CIBLE = D2.SRVPGM_SOURCE

INNER JOIN QTEMP.SRVPGM_DEPS D3

  ON D2.LIB_CIBLE = D3.LIB_SOURCE

  AND D2.SRVPGM_CIBLE = D3.SRVPGM_SOURCE

WHERE D3.LIB_CIBLE = D1.LIB_SOURCE

  AND D3.SRVPGM_CIBLE = D1.SRVPGM_SOURCE

  AND D1.SRVPGM_SOURCE <> D2.SRVPGM_CIBLE  -- Pas de boucle immédiate

  AND D2.SRVPGM_CIBLE <> D3.SRVPGM_CIBLE   -- Pas de doublon


ORDER BY TYPE_CYCLE, SRVPGM_A;

```


### Résultat attendu

```

TYPE_CYCLE      SRVPGM_A            SRVPGM_B                    RELATION

--------------  ------------------  --------------------------  ---------------------

CYCLE DIRECT    MYLIB/CLIENT        MYLIB/FACTURE               ←→

CYCLE DIRECT    MYLIB/ADRESSE       MYLIB/COMMANDE              ←→

CYCLE INDIRECT  MYLIB/PRODUIT       MYLIB/PRODUIT               PRODUIT → STOCK → PRIX → PRODUIT

Solution 2 : Vue SQL réutilisable

Pour un audit permanent, créer une vue SQL :


-- =========================================================

-- VUE : TOUTES LES DÉPENDANCES ENTRE SRVPGM

-- =========================================================


CREATE OR REPLACE VIEW MYLIB.V_SRVPGM_DEPENDENCIES AS

SELECT

  TRIM(A.SYSTEM_OBJECT_SCHEMA) AS SOURCE_LIB,

  TRIM(A.SYSTEM_OBJECT_NAME) AS SOURCE_SRVPGM,

  TRIM(A.OBJECT_TEXT) AS SOURCE_DESC,

  TRIM(B.BOUND_OBJECT_LIBRARY) AS TARGET_LIB,

  TRIM(B.BOUND_OBJECT) AS TARGET_SRVPGM,

  B.BOUND_OBJECT_TYPE AS TARGET_TYPE,

  B.ACTIVATION_GROUP AS ACTGRP,

  B.BINDING_DIRECTORY AS BNDDIR

FROM QSYS2.PROGRAM_INFO A

CROSS JOIN LATERAL (

  SELECT * FROM TABLE(QSYS2.BOUND_SRVPGM_INFO(

    SERVICE_PROGRAM_LIBRARY => A.SYSTEM_OBJECT_SCHEMA,

    SERVICE_PROGRAM => A.SYSTEM_OBJECT_NAME

  ))

) B

WHERE A.OBJECT_TYPE = '*SRVPGM'

  AND B.BOUND_OBJECT_TYPE = '*SRVPGM';


-- =========================================================

-- VUE : CYCLES DIRECTS DÉTECTÉS

-- =========================================================


CREATE OR REPLACE VIEW MYLIB.V_SRVPGM_CIRCULAR_DEPS AS

SELECT

  D1.SOURCE_LIB || '/' || D1.SOURCE_SRVPGM AS SRVPGM_A,

  D1.TARGET_LIB || '/' || D1.TARGET_SRVPGM AS SRVPGM_B,

  'Direct circular dependency' AS ISSUE_TYPE,

  CURRENT_TIMESTAMP AS DETECTED_AT

FROM MYLIB.V_SRVPGM_DEPENDENCIES D1

INNER JOIN MYLIB.V_SRVPGM_DEPENDENCIES D2

  ON D1.SOURCE_LIB = D2.TARGET_LIB

  AND D1.SOURCE_SRVPGM = D2.TARGET_SRVPGM

  AND D1.TARGET_LIB = D2.SOURCE_LIB

  AND D1.TARGET_SRVPGM = D2.SOURCE_SRVPGM

WHERE D1.SOURCE_SRVPGM < D2.SOURCE_SRVPGM;  -- Évite doublons

Et hop ! Exécution de la vue !


-- Voir toutes les dépendances

SELECT * FROM MYLIB.V_SRVPGM_DEPENDENCIES

WHERE SOURCE_LIB = 'MYLIB'

ORDER BY SOURCE_SRVPGM, TARGET_SRVPGM;


-- Voir uniquement les cycles

SELECT * FROM MYLIB.V_SRVPGM_CIRCULAR_DEPS;

Solution 3 : Procédure stockée avec graphe complet

-- =========================================================

-- PROCÉDURE : ANALYSER LES CYCLES À N NIVEAUX

-- =========================================================


CREATE OR REPLACE PROCEDURE MYLIB.CHECK_SRVPGM_CYCLES(

  IN P_LIBRARY VARCHAR(10)

)

LANGUAGE SQL

BEGIN

 

  -- Table temporaire pour stocker les chemins

  DECLARE GLOBAL TEMPORARY TABLE SESSION.PATHS (

    LEVEL INT,

    PATH VARCHAR(1000),

    SOURCE_SRVPGM VARCHAR(10),

    TARGET_SRVPGM VARCHAR(10),

    IS_CYCLE CHAR(1)

  ) WITH REPLACE;

 

  -- 1. Récupérer toutes les dépendances directes

  INSERT INTO SESSION.PATHS (LEVEL, PATH, SOURCE_SRVPGM, TARGET_SRVPGM, IS_CYCLE)

  SELECT

    1 AS LEVEL,

    SOURCE_SRVPGM || ' → ' || TARGET_SRVPGM AS PATH,

    SOURCE_SRVPGM,

    TARGET_SRVPGM,

    'N' AS IS_CYCLE

  FROM MYLIB.V_SRVPGM_DEPENDENCIES

  WHERE SOURCE_LIB = P_LIBRARY

    AND TARGET_LIB = P_LIBRARY;

 

  -- 2. Récursion pour trouver les chemins à 2+ niveaux

  FOR V_LEVEL AS cur CURSOR FOR

    SELECT LEVEL FROM (VALUES(2),(3),(4),(5)) AS T(LEVEL)

  DO

    INSERT INTO SESSION.PATHS (LEVEL, PATH, SOURCE_SRVPGM, TARGET_SRVPGM, IS_CYCLE)

    SELECT

      V_LEVEL AS LEVEL,

      P.PATH || ' → ' || D.TARGET_SRVPGM AS PATH,

      P.SOURCE_SRVPGM,

      D.TARGET_SRVPGM,

      CASE

        WHEN D.TARGET_SRVPGM = P.SOURCE_SRVPGM THEN 'Y'  -- Cycle détecté !

        WHEN POSITION(D.TARGET_SRVPGM IN P.PATH) > 0 THEN 'Y'  -- Déjà visité

        ELSE 'N'

      END AS IS_CYCLE

    FROM SESSION.PATHS P

    INNER JOIN MYLIB.V_SRVPGM_DEPENDENCIES D

      ON P.TARGET_SRVPGM = D.SOURCE_SRVPGM

      AND P.LEVEL = V_LEVEL - 1

      AND P.IS_CYCLE = 'N'  -- Ne continue que les chemins sans cycle

    WHERE D.SOURCE_LIB = P_LIBRARY

      AND D.TARGET_LIB = P_LIBRARY

      AND POSITION(D.TARGET_SRVPGM IN P.PATH) = 0;  -- Évite boucles infinies

  END FOR;

 

  -- 3. Afficher uniquement les cycles

  SELECT

    LEVEL AS CYCLE_DEPTH,

    PATH AS CIRCULAR_PATH,

    SOURCE_SRVPGM AS STARTS_AT,

    TARGET_SRVPGM AS ENDS_AT

  FROM SESSION.PATHS

  WHERE IS_CYCLE = 'Y'

  ORDER BY LEVEL, PATH;

 

END;


Utilisation : 


CALL MYLIB.CHECK_SRVPGM_CYCLES('MYLIB');

```


**Résultat :**

```

CYCLE_DEPTH  CIRCULAR_PATH                              STARTS_AT  ENDS_AT

-----------  -----------------------------------------  ---------  ---------

2            CLIENT → FACTURE → CLIENT                  CLIENT     CLIENT

3            PRODUIT → STOCK → PRIX → PRODUIT           PRODUIT    PRODUIT

4            A → B → C → D → A                          A          A

Anatomie du STRPGMEXP

Le STRPGMEXP (Start Program Export) est un outil puissant mais souvent mal utilisé. 

C'est le "contrat d'interface" du *SRVPGM.

Voyons les bonnes pratiques en détail ! 📜

Structure de base

// QSRVSRC/CUSTOMER - Binder source

**free


STRPGMEXP PGMLVL(*CURRENT) SIGNATURE('CUSTOMER_V2_0_0')


  EXPORT SYMBOL(validerEmail)

  EXPORT SYMBOL(validerSiret)

  EXPORT SYMBOL(creerClient)

  EXPORT SYMBOL(supprimerClient)

  EXPORT SYMBOL(calculerScoreFidelite)


ENDPGMEXP

```


### Éléments clés

```

STRPGMEXP PGMLVL(*CURRENT) SIGNATURE('...')

   │         │                  │

   │         │                  └─ Signature unique pour cette version

   │         └─ Niveau de programme (*CURRENT ou *PRV)

   └─ Début de la définition d'export


EXPORT SYMBOL(nomProcedure)

   │       │

   │       └─ Nom exact de la procédure à exporter

   └─ Directive d'export


ENDPGMEXP

   └─ Fin de la définition d'export

Bonne pratique #1 : Versioning sémantique de la signature

❌ Mauvais : Signature générique

STRPGMEXP PGMLVL(*CURRENT) SIGNATURE('CUSTOMER')

Problème : Impossible de savoir quelle version, aucune traçabilité.

✅ Bon : Signature versionnée

// Format recommandé: NOMLIB_NOM_VMAJOR_MINOR_PATCH

STRPGMEXP PGMLVL(*CURRENT) SIGNATURE('CUSTOMER_V2_0_0')

Avantages :

  • ✅ Traçabilité claire des versions

  • ✅ Compatible avec versioning sémantique

  • ✅ Facile à auditer

🎯 Excellent : Signature avec date

// Format: NOMLIB_NOM_VMAJOR_MINOR_YYYYMMDD

STRPGMEXP PGMLVL(*CURRENT) SIGNATURE('CUST_V2_0_20260122')

Quand incrémenter la version ?


Changement

Version

Exemple

Bug fix interne (procédure privée)

PATCH (2.0.0 → 2.0.1)

Correction algo validation

Ajout procédure (compatible)

MINOR (2.0.0 → 2.1.0)

Nouvelle fonction validerIBAN

Suppression/modification signature

MAJOR (2.0.0 → 3.0.0)

creerClient change params



Bonne pratique #2 : Gestion multi-niveaux (*CURRENT + *PRV)

Concept : Compatibilité ascendante

Permet aux anciens programmes de continuer à fonctionner tout en offrant de nouvelles fonctionnalités.

// QSRVSRC/CUSTOMER

**free

//===========================================================

// Version CURRENT (3.0.0) - Latest

//===========================================================

STRPGMEXP PGMLVL(*CURRENT) SIGNATURE('CUSTOMER_V3_0_0')


  // Exports V1.0

  EXPORT SYMBOL(validerEmail)

  EXPORT SYMBOL(validerSiret)

  EXPORT SYMBOL(creerClient)

  EXPORT SYMBOL(supprimerClient)

 

  // Exports V2.0 (ajouts compatibles)

  EXPORT SYMBOL(calculerScoreFidelite)

  EXPORT SYMBOL(verifierLimiteCredit)

 

  // Exports V3.0 (nouvelles fonctionnalités)

  EXPORT SYMBOL(creerClientComplet)      // Nouvelle version étendue

  EXPORT SYMBOL(obtenirHistoriqueClient)

  EXPORT SYMBOL(archiverClient)


ENDPGMEXP


//===========================================================

// Version PRV (2.0.0) - Previous (pour compatibilité)

//===========================================================

STRPGMEXP PGMLVL(*PRV) SIGNATURE('CUSTOMER_V2_0_0')


  EXPORT SYMBOL(validerEmail)

  EXPORT SYMBOL(validerSiret)

  EXPORT SYMBOL(creerClient)

  EXPORT SYMBOL(supprimerClient)

  EXPORT SYMBOL(calculerScoreFidelite)

  EXPORT SYMBOL(verifierLimiteCredit)


ENDPGMEXP

```

Comment ça marche ?


Programme bindé avec V2 → Utilise PGMLVL(*PRV)

  ├─ Trouve signature 'CUSTOMER_V2_0_0'

  └─ Accède aux 6 procédures de V2


Programme bindé avec V3 → Utilise PGMLVL(*CURRENT)

  ├─ Trouve signature 'CUSTOMER_V3_0_0'

  └─ Accède aux 9 procédures de V3


Les deux cohabitent dans le MÊME *SRVPGM ! ✅


Limite : Maximum 2 niveaux

IBM i supporte uniquement :

  • *CURRENT (version courante)

  • *PRV (version précédente)

Pas de *PRV2, *PRV3... → Si on veut supporter plus de 2 versions, il faut créer des *SRVPGM séparés.

Bonne pratique #3 : Organisation et documentation

✅ Bon : Grouper par version et commenter

**free

//===========================================================

// Version 3.0.0 - CURRENT (2026-01-22)

// Ajouts:

//   - creerClientComplet (avec adresse et facturation)

//   - obtenirHistoriqueClient

//   - archiverClient

//===========================================================

STRPGMEXP PGMLVL(*CURRENT) SIGNATURE('CUSTOMER_V3_0_0')


  //--- V1.0 - Exports initiaux (2024-03-15) ---

  EXPORT SYMBOL(validerEmail)

  EXPORT SYMBOL(validerSiret)

  EXPORT SYMBOL(creerClient)

  EXPORT SYMBOL(supprimerClient)

 

  //--- V2.0 - Scoring et limites (2025-06-10) ---

  EXPORT SYMBOL(calculerScoreFidelite)

  EXPORT SYMBOL(verifierLimiteCredit)

 

  //--- V3.0 - Gestion avancée (2026-01-22) ---

  EXPORT SYMBOL(creerClientComplet)

  EXPORT SYMBOL(obtenirHistoriqueClient)

  EXPORT SYMBOL(archiverClient)


ENDPGMEXP


//===========================================================

// Version 2.0.0 - PREVIOUS (2025-06-10)

// Compatibilité pour programmes existants

//===========================================================

STRPGMEXP PGMLVL(*PRV) SIGNATURE('CUSTOMER_V2_0_0')


  EXPORT SYMBOL(validerEmail)

  EXPORT SYMBOL(validerSiret)

  EXPORT SYMBOL(creerClient)

  EXPORT SYMBOL(supprimerClient)

  EXPORT SYMBOL(calculerScoreFidelite)

  EXPORT SYMBOL(verifierLimiteCredit)


ENDPGMEXP

Avantages :

  • ✅ Historique clair des évolutions

  • ✅ Facile de voir ce qui a été ajouté quand

  • ✅ Documentation intégrée au source

❌ Mauvais : Liste en vrac sans structure

STRPGMEXP PGMLVL(*CURRENT) SIGNATURE('CUSTOMER')

  EXPORT SYMBOL(creerClient)

  EXPORT SYMBOL(calculerScoreFidelite)

  EXPORT SYMBOL(validerEmail)

  EXPORT SYMBOL(archiverClient)

  EXPORT SYMBOL(supprimerClient)

  EXPORT SYMBOL(validerSiret)

ENDPGMEXP


Problèmes :

  • ❌ Pas d'ordre logique

  • ❌ Impossible de savoir quand chaque export a été ajouté

  • ❌ Difficile de maintenir

Bonne pratique #4 : Stratégie de dépréciation

Scénario : Remplacer creerClient par creerClientV2

Étape 1 : Ajouter nouvelle version (compatibilité totale)

**free

//===========================================================

// Version 2.0.0 - CURRENT

//===========================================================

STRPGMEXP PGMLVL(*CURRENT) SIGNATURE('CUSTOMER_V2_0_0')


  //--- V1.0 - Exports initiaux ---

  EXPORT SYMBOL(validerEmail)

  EXPORT SYMBOL(validerSiret)

  EXPORT SYMBOL(creerClient)        // DEPRECATED - Utiliser creerClientV2

  EXPORT SYMBOL(supprimerClient)

 

  //--- V2.0 - Nouvelles versions ---

  EXPORT SYMBOL(creerClientV2)      // Nouvelle version recommandée


ENDPGMEXP


//===========================================================

// Version 1.0.0 - PREVIOUS

//===========================================================

STRPGMEXP PGMLVL(*PRV) SIGNATURE('CUSTOMER_V1_0_0')


  EXPORT SYMBOL(validerEmail)

  EXPORT SYMBOL(validerSiret)

  EXPORT SYMBOL(creerClient)

  EXPORT SYMBOL(supprimerClient)


ENDPGMEXP


Dans le module CUSTCRUD :


// Ancienne version - DEPRECATED

dcl-proc creerClient export;

  dcl-pi *n packed(9:0);

    nom varchar(50) const;

    email varchar(100) const;

  end-pi;

 

  // Log warning

  logWarn('creerClient est obsolète, utilisez creerClientV2');

 

  // Délègue à la nouvelle version avec param par défaut

  return creerClientV2(nom : email : '');

end-proc;


// Nouvelle version

dcl-proc creerClientV2 export;

  dcl-pi *n packed(9:0);

    nom varchar(50) const;

    email varchar(100) const;

    siret char(14) const;

  end-pi;

 

  // Implémentation complète

  // ...

  return clientId;

end-proc;


*Étape 2 (6 mois plus tard) : Retirer de CURRENT


//===========================================================

// Version 3.0.0 - CURRENT (creerClient retiré)

//===========================================================

STRPGMEXP PGMLVL(*CURRENT) SIGNATURE('CUSTOMER_V3_0_0')


  EXPORT SYMBOL(validerEmail)

  EXPORT SYMBOL(validerSiret)

  // EXPORT SYMBOL(creerClient)  ← RETIRÉ de *CURRENT

  EXPORT SYMBOL(supprimerClient)

  EXPORT SYMBOL(creerClientV2)


ENDPGMEXP


//===========================================================

// Version 2.0.0 - PREVIOUS (garde creerClient pour compat)

//===========================================================

STRPGMEXP PGMLVL(*PRV) SIGNATURE('CUSTOMER_V2_0_0')


  EXPORT SYMBOL(validerEmail)

  EXPORT SYMBOL(validerSiret)

  EXPORT SYMBOL(creerClient)        // Encore là pour anciens programmes

  EXPORT SYMBOL(supprimerClient)

  EXPORT SYMBOL(creerClientV2)


ENDPGMEXP


Résultat :

  • Nouveaux programmes → Doivent utiliser creerClientV2

  • Anciens programmes → Continuent de fonctionner (bindés avec *PRV)

Étape 3 (12 mois plus tard) : Retrait total

//===========================================================
// Version 4.0.0 - CURRENT
//===========================================================

STRPGMEXP PGMLVL(*CURRENT) SIGNATURE('CUSTOMER_V4_0_0')
  EXPORT SYMBOL(validerEmail)
  EXPORT SYMBOL(validerSiret)
  EXPORT SYMBOL(supprimerClient)
  EXPORT SYMBOL(creerClientV2)

ENDPGMEXP

//===========================================================
// Version 3.0.0 - PREVIOUS
//===========================================================

STRPGMEXP PGMLVL(*PRV) SIGNATURE('CUSTOMER_V3_0_0')
  EXPORT SYMBOL(validerEmail)
  EXPORT SYMBOL(validerSiret)
  // EXPORT SYMBOL(creerClient)  ← RETIRÉ aussi de *PRV
  EXPORT SYMBOL(supprimerClient)
  EXPORT SYMBOL(creerClientV2)

ENDPGMEXP


À ce stade : creerClient n'existe plus nulle part → les programmes doivent avoir été migrés.

Bonne pratique #5 : Ordre des exports

Principe : Une fois exporté, ne JAMAIS changer l'ordre

// ❌ ERREUR GRAVE

// Version 1.0

STRPGMEXP PGMLVL(*CURRENT) SIGNATURE('CUSTOMER_V1_0_0')

  EXPORT SYMBOL(validerEmail)      // Position 1

  EXPORT SYMBOL(creerClient)       // Position 2

  EXPORT SYMBOL(supprimerClient)   // Position 3

ENDPGMEXP


// Version 2.0 - ORDRE CHANGÉ !

STRPGMEXP PGMLVL(*CURRENT) SIGNATURE('CUSTOMER_V2_0_0')

  EXPORT SYMBOL(creerClient)       // Position 1 (était 2)

  EXPORT SYMBOL(validerEmail)      // Position 2 (était 1)

  EXPORT SYMBOL(supprimerClient)   // Position 3 (inchangé)

  EXPORT SYMBOL(nouvelleProc)      // Position 4

ENDPGMEXP


Conséquence : Programmes peuvent appeler la mauvaise procédure ! 💥

✅ Bon : Toujours ajouter à la fin

// Version 1.0

STRPGMEXP PGMLVL(*CURRENT) SIGNATURE('CUSTOMER_V1_0_0')

  EXPORT SYMBOL(validerEmail)      // Position 1

  EXPORT SYMBOL(creerClient)       // Position 2

  EXPORT SYMBOL(supprimerClient)   // Position 3

ENDPGMEXP


// Version 2.0 - Ajout à la FIN

STRPGMEXP PGMLVL(*CURRENT) SIGNATURE('CUSTOMER_V2_0_0')

  EXPORT SYMBOL(validerEmail)      // Position 1 (inchangé)

  EXPORT SYMBOL(creerClient)       // Position 2 (inchangé)

  EXPORT SYMBOL(supprimerClient)   // Position 3 (inchangé)

  EXPORT SYMBOL(nouvelleProc)      // Position 4 (nouveau)

ENDPGMEXP


Important : Cette règle s'applique au sein d'un même niveau (CURRENT ou PRV).

Bonne pratique #6 : Utiliser EXPORT(*SRCFILE) vs EXPORT(*ALL)

Comparaison


/* Option A : EXPORT(*ALL) - Automatique */

CRTSRVPGM SRVPGM(MYLIB/CUSTOMER) +

          MODULE(MYLIB/CUSTVAL MYLIB/CUSTCRUD) +

          EXPORT(*ALL)


/* Option B : EXPORT(*SRCFILE) - Contrôlé */

CRTSRVPGM SRVPGM(MYLIB/CUSTOMER) +

          MODULE(MYLIB/CUSTVAL MYLIB/CUSTCRUD) +

          SRCFILE(MYLIB/QSRVSRC) +

          SRCMBR(CUSTOMER) +

          EXPORT(*SRCFILE)


EXPORT(*ALL) - Automatique

Ce que ça fait :

  • Exporte toutes les procédures marquées export dans tous les modules

  • Génère automatiquement une signature

Avantages :

  • ✅ Simple, pas de fichier binder à gérer

  • ✅ Bon pour prototypage rapide

Inconvénients :

  • ❌ Aucun contrôle sur ce qui est exporté

  • ❌ Signature auto-générée (pas de versioning)

  • ❌ Impossible de gérer *CURRENT + *PRV

  • ❌ Pas de documentation des exports

Quand utiliser :

  • Phase de développement/prototypage

  • *SRVPGM interne non partagé

  • Petit projet mono-personne

EXPORT(*SRCFILE) - Contrôlé ✅ Recommandé

Ce que ça fait :

  • Exporte uniquement ce qui est listé dans STRPGMEXP

  • Utilise la signature définie dans le binder source

Avantages :

  • ✅ Contrôle total sur les exports

  • ✅ Versioning avec *CURRENT + *PRV

  • ✅ Documentation intégrée

  • ✅ Sécurité (pas d'export accidentel)

Inconvénients :

  • ⚠️ Fichier supplémentaire à maintenir

  • ⚠️ Doit être synchronisé avec les procédures réelles

Quand utiliser :

  • Production

  • *SRVPGM partagé entre équipes

  • Projet avec versioning strict

  • Toujours recommandé pour du code professionnel

Bonne pratique #7 : Validation des exports

Problème : Export listé mais procédure inexistante


// QSRVSRC/CUSTOMER

STRPGMEXP PGMLVL(*CURRENT) SIGNATURE('CUSTOMER_V2_0_0')

  EXPORT SYMBOL(validerEmail)

  EXPORT SYMBOL(creerClient)

  EXPORT SYMBOL(procInexistante)  // ← ERREUR : n'existe pas !

ENDPGMEXP

```

**Résultat lors de CRTSRVPGM :**

```

CPF5D02: Symbol procInexistante not found


Solution : Script de validation


-- Vérifier que tous les exports existent réellement

-- (nécessite de parser le binder source, exemple simplifié)


-- Liste des procédures effectivement exportées

SELECT DISTINCT TRIM(EXPORT_SYMBOL_NAME) AS PROCEDURE_NAME

FROM TABLE(QSYS2.SRVPGM_EXPORT_INFO(

  SERVICE_PROGRAM_LIBRARY => 'MYLIB',

  SERVICE_PROGRAM => 'CUSTOMER'

))

ORDER BY PROCEDURE_NAME;


Intégration dans build :

/* Script de build */

PGM
  /* 1. Compiler les modules */
CRTRPGMOD MODULE(MYLIB/CUSTVAL) …
CRTRPGMOD MODULE(MYLIB/CUSTCRUD) ...

  /* 2. Créer le SRVPGM */
  CRTSRVPGM SRVPGM(MYLIB/CUSTOMER) +
           SRCFILE(MYLIB/QSRVSRC) +
           SRCMBR(CUSTOMER) +
           EXPORT(*SRCFILE)

  MONMSG MSGID(CPF5D02) EXEC(DO)
    SNDPGMMSG MSG('Export introuvable dans binder source !') +
             MSGTYPE(*ESCAPE)
  ENDDO

  /* 3. Valider les exports */
  RUNSQL SQL('SELECT COUNT(*) FROM TABLE(...)')

ENDPGM

## Bonne pratique #8 : Convention de nommage des signatures

### Format recommandé

```
[BIBLIOTHEQUE]_[SRVPGM]_V[MAJOR]_[MINOR]_[PATCH/DATE]


Exemples :

// Avec patch version

SIGNATURE('MYLIB_CUSTOMER_V2_0_1')

// Avec date

SIGNATURE('MYLIB_CUSTOMER_V2_0_20260122')

// Court (si lib = srvpgm)

SIGNATURE('CUSTOMER_V2_0_0')

// Avec suffixe fonctionnel

SIGNATURE('CUSTOMER_CRUD_V1_0_0')
SIGNATURE('CUSTOMER_VALID_V1_0_0')


❌ À éviter

// Trop générique

SIGNATURE('CUSTOMER')

// Pas de version

SIGNATURE('CUSTOMER_PROD')

// Version non standard

SIGNATURE('CUSTOMER_VERSION2')

// Trop long (max 256 caractères mais illisible)

SIGNATURE('MYCOMPANY_MYLIB_CUSTOMER_SRVPGM_VERSION_2_POINT_0_RELEASE_CANDIDATE_1')


Bonne pratique #9 : Gestion des data areas / variables exportées

Exporter des variables (rare mais possible)

// Module CUSTCFG
**free
ctl-opt nomain;

// Variable globale exportée
dcl-s tauxTVADefaut packed(5:2) export;
dcl-proc initConfig export;
  tauxTVADefaut = 20.0;
end-proc;


Binder source :

STRPGMEXP PGMLVL(*CURRENT) SIGNATURE('CONFIG_V1_0_0')
  EXPORT SYMBOL(initConfig)
  EXPORT SYMBOL(tauxTVADefaut)  // Export de la variable
ENDPGMEXP


Utilisation :

// Programme utilisateur

**free
/copy qrpglesrc,config_h

dcl-s montantTTC packed(11:2);

initConfig();
montantTTC = 100.0 * (1 + tauxTVADefaut / 100);
// montantTTC = 120.00


⚠️ Attention : Variables exportées = état global

Problèmes potentiels :

  • ❌ État partagé entre tous les programmes

  • ❌ Difficile à tester

  • ❌ Risques de concurrence

Recommandation : Préférer des procédures getter/setter plutôt que des variables exportées.

// ✅ Meilleure approche

dcl-proc getTauxTVADefaut export;
  dcl-pi *n packed(5:2);
  end-pi;
  return 20.0;
end-proc;

dcl-proc setTauxTVADefaut export;
  dcl-pi *n;
    nouveauTaux packed(5:2) const;
  end-pi;
  // Validation + setter
end-proc;


Bonne pratique #10 : Template de binder source

Template complet réutilisable

**free

//===========================================================
// [NOM_SRVPGM] - Export definitions
//
// Description:
//   [Courte description de la responsabilité du SRVPGM]
//===========================================================
// Version [MAJOR].[MINOR].[PATCH] - CURRENT ([DATE])
//
// Changes in this version:
//   - [Liste des changements]
//===========================================================

STRPGMEXP PGMLVL(*CURRENT) SIGNATURE('[NOM]_V[X]_[Y]_[Z]')

  //--- V1.0 - Initial exports ([DATE]) —
  EXPORT SYMBOL(procedure1)
  EXPORT SYMBOL(procedure2)
  //--- V2.0 - New features ([DATE]) —
  EXPORT SYMBOL(procedure3)
  EXPORT SYMBOL(procedure4)
ENDPGMEXP

//===========================================================
// Version [MAJOR_PRV].[MINOR_PRV].[PATCH_PRV] - PREVIOUS
// Maintained for backward compatibility
// Deprecated: [Date de fin de support prévue]
//===========================================================
STRPGMEXP PGMLVL(*PRV) SIGNATURE('[NOM]_V[X]_[Y]_[Z]')
  EXPORT SYMBOL(procedure1)
  EXPORT SYMBOL(procedure2)
ENDPGMEXP


Exemple concret d'utilisation

**free
//===========================================================
// CUSTOMER - Export definitions
//
// Description:
//   Gestion des clients: validation, CRUD, scoring
// Change history:
//   V1.0.0 - 2024-03-15 - Pacôme - Création initiale (validation + CRUD)
//   V2.0.0 - 2025-06-10 - Abélar - Ajout scoring et limites de crédit
//   V3.0.0 - 2026-01-22 - Hégésipe - Gestion avancée et archivage
//===========================================================
// Version 3.0.0 - CURRENT (2026-01-22)
//
// Changes in this version:
//   - Added creerClientComplet (full client creation with address)
//   - Added obtenirHistoriqueClient (client history retrieval)
//   - Added archiverClient (soft delete with audit trail)
//===========================================================
STRPGMEXP PGMLVL(*CURRENT) SIGNATURE('CUSTOMER_V3_0_0')
  //--- V1.0 - Initial exports (2024-03-15) —
  EXPORT SYMBOL(validerEmail)
  EXPORT SYMBOL(validerSiret)
  EXPORT SYMBOL(creerClient)
  EXPORT SYMBOL(supprimerClient)
  //--- V2.0 - Scoring & Credit (2025-06-10) —
  EXPORT SYMBOL(calculerScoreFidelite)
  EXPORT SYMBOL(verifierLimiteCredit)
  //--- V3.0 - Advanced Management (2026-01-22) —
 EXPORT SYMBOL(creerClientComplet)
  EXPORT SYMBOL(obtenirHistoriqueClient)
  EXPORT SYMBOL(archiverClient)
ENDPGMEXP

//===========================================================
// Version 2.0.0 - PREVIOUS (2025-06-10)
//
// Maintained for backward compatibility
// Deprecated: End of support planned for 2027-06-10
//===========================================================

STRPGMEXP PGMLVL(*PRV) SIGNATURE('CUSTOMER_V2_0_0')
  EXPORT SYMBOL(validerEmail)
  EXPORT SYMBOL(validerSiret)
  EXPORT SYMBOL(creerClient)
  EXPORT SYMBOL(supprimerClient)
  EXPORT SYMBOL(calculerScoreFidelite)
  EXPORT SYMBOL(verifierLimiteCredit)
ENDPGMEXP

```

## Checklist finale : STRPGMEXP

Avant de compiler le *SRVPGM avec EXPORT(*SRCFILE) :

```

☐ Signature versionnée (format: NOM_VMAJOR_MINOR_PATCH)
☐ Commentaires de version avec date et auteur
☐ Exports groupés par version d'ajout
☐ Ordre des exports JAMAIS modifié (ajouts à la fin)
☐ PGMLVL(*PRV) si besoin de compatibilité
☐ Documentation du changelog
☐ Validation que tous les exports existent réellement
☐ Convention de nommage cohérente
☐ Plan de dépréciation si retrait prévu

Résumé ultra-synthétique

STRPGMEXP - Bonnes pratiques :

  1. Signature versionnée : CUSTOMER_V2_0_0 (pas juste CUSTOMER)

  2. Multi-niveaux : *CURRENT + *PRV pour compatibilité

  3. Organisation : Grouper exports par version, commenter

  4. Dépréciation progressive : CURRENT → PRV → Retrait total

  5. Ordre invariant : Ne JAMAIS changer l'ordre, toujours ajouter à la fin

  6. *EXPORT(SRCFILE) : Privilégier vs *ALL pour le contrôle

  7. Validation : Vérifier que tous les exports existent

  8. Nommage cohérent : Standard d'équipe pour signatures

  9. Variables exportées : Éviter, préférer getters/setters

  10. Template : Utiliser un template avec changelog

Le STRPGMEXP est le contrat d'interface → A traiter avec autant de soin qu'une API REST publique ! 📜

✅ Architecture en couches (Infrastructure → Services → Métier)

✅ Détection des dépendances circulaires (SQL)

✅ Gestion des modules (ajout, modification)

✅ Procédures privées vs publiques

✅ STRPGMEXP et versioning sémantique


Commentaires

Articles les plus consultés