Un seul objet Salesforce peut se comporter comme plusieurs entités différentes selon son RecordTypeId. Quand les types d’enregistrement représentent des processus métier distincts — des séquences d’étapes, des règles de validation ou des métriques différentes — les modéliser comme une seule entité produit des résultats mixtes qui ne sont pas utiles pour l’analyse par type d’enregistrement.
Ce que font les types d’enregistrement dans Salesforce
Les types d’enregistrement permettent aux admins Salesforce de définir différentes « variantes » du même objet. L’exemple le plus courant : les types d’enregistrement d’Opportunity comme « Nouvelle affaire », « Renouvellement » et « Upsell ». Chaque type d’enregistrement peut avoir :
- Des valeurs de picklist différentes — les noms d’étapes pour les nouvelles opportunités pourraient être Prospecting → Qualification → Proposal → Negotiation → Closed Won. Les étapes de renouvellement pourraient être Upcoming → In Review → Approved → Closed Won. Même champ (
StageName), ensembles de valeurs complètement différents. - Des règles de validation différentes — les nouvelles affaires peuvent exiger une date de discovery, les renouvellements peuvent exiger une durée de renouvellement.
- Des mises en page différentes — les commerciaux voient des champs différents selon le type d’enregistrement.
Depuis l’interface Salesforce, ces éléments ressemblent à des objets différents. Depuis la base de données, ce sont toutes des lignes dans la même table Opportunity, différenciées uniquement par la colonne RecordTypeId.
Quand diviser
La décision dépend de savoir si les types d’enregistrement représentent des processus métier genuinement différents ou des variations mineures du même. Si les noms d’étapes, les délais du cycle et les métriques clés diffèrent selon le type d’enregistrement (par exemple, Nouvelle affaire vs. Renouvellement), la division est appropriée. Si les types d’enregistrement ne diffèrent que dans la mise en page ou les règles de validation mais partagent la même structure de pipeline, un seul modèle avec une colonne de type est suffisant.
Diviser par type d’enregistrement dans les modèles de base
Quand les types d’enregistrement représentent des pipelines différents, créez des modèles de base séparés filtrés par RecordTypeId :
-- base__salesforce__opportunity_new_business.sqlSELECT id AS opportunity__id, account_id AS opportunity__account_id, owner_id AS opportunity__owner_id, name AS opportunity__name, stage_name AS opportunity__stage, amount AS opportunity__amount, close_date AS opportunity__close_at, is_won AS opportunity__is_won, is_closed AS opportunity__is_closed, created_date AS opportunity__created_atFROM {{ source('salesforce', 'opportunity') }}WHERE NOT _fivetran_deleted AND record_type_id = '{{ var("salesforce_new_business_record_type_id") }}'-- base__salesforce__opportunity_renewal.sqlSELECT id AS opportunity__id, account_id AS opportunity__account_id, owner_id AS opportunity__owner_id, name AS opportunity__name, stage_name AS opportunity__stage, amount AS opportunity__amount, close_date AS opportunity__close_at, is_won AS opportunity__is_won, is_closed AS opportunity__is_closed, created_date AS opportunity__created_atFROM {{ source('salesforce', 'opportunity') }}WHERE NOT _fivetran_deleted AND record_type_id = '{{ var("salesforce_renewal_record_type_id") }}'Chaque modèle est presque identique, mais la clause WHERE sur record_type_id les sépare. Les modèles intermédiaires et mart en aval peuvent ensuite appliquer une logique spécifique au type d’enregistrement — des mappings d’étapes différents, des métriques différentes, une granularité différente.
C’est un jugement sur où placer la division. Certaines équipes préfèrent filtrer au niveau de la couche intermédiaire à la place, en gardant un seul modèle de base pour toutes les opportunités et en bifurquant plus tard. Les deux fonctionnent. La division au niveau de la couche de base est plus propre quand les types d’enregistrement représentent vraiment des entités différentes — cela rend votre DAG honnête sur ce que chaque branche représente.
Stocker les IDs de types d’enregistrement dans les vars dbt
Les IDs de types d’enregistrement sont des IDs Salesforce de 18 caractères que votre admin Salesforce assigne lors de la création des types d’enregistrement. Ils sont stables — ils ne changent pas sauf si quelqu’un supprime et recrée le type d’enregistrement — mais ils sont opaques. Personne qui lit votre SQL ne sait ce que signifie 0125f000000abCDEFG.
Stockez-les dans dbt_project.yml comme vars :
vars: salesforce_new_business_record_type_id: "0125f000000abCDEFG" salesforce_renewal_record_type_id: "0125f000000ghIJKLM" salesforce_upsell_record_type_id: "0125f000000noPQRST"Cela vous donne :
- Documentation — le nom de la var explique ce que chaque ID signifie.
- Point de changement unique — quand votre admin Salesforce crée un nouveau type d’enregistrement, vous mettez à jour un seul fichier.
- Flexibilité d’environnement — les orgs Salesforce sandbox et production peuvent avoir des IDs de types d’enregistrement différents. Vous pouvez surcharger les vars par environnement dans vos profils dbt.
L’alternative : un seul modèle avec une colonne de type
Si vos types d’enregistrement partagent la même structure de pipeline et que vos parties prenantes les voient comme des sous-ensembles de la même entité plutôt que des entités séparées, un seul modèle avec une colonne record_type fonctionne bien :
-- base__salesforce__opportunity.sqlSELECT id AS opportunity__id, account_id AS opportunity__account_id, record_type_id AS opportunity__record_type_id, -- ... autres champsFROM {{ source('salesforce', 'opportunity') }}WHERE NOT _fivetran_deletedPuis joignez vers une table de référence RecordType dans un modèle intermédiaire pour obtenir des noms lisibles :
-- int__opportunity_with_record_type.sqlSELECT opp.*, rt.name AS opportunity__record_type_nameFROM {{ ref('base__salesforce__opportunity') }} AS oppLEFT JOIN {{ ref('base__salesforce__record_type') }} AS rt ON opp.opportunity__record_type_id = rt.record_type__idCela préserve un lignage d’opportunités unique tout en rendant le type d’enregistrement disponible pour le filtrage dans les dashboards et les modèles mart.
Types d’enregistrement au-delà des opportunités
Opportunity est l’objet le plus courant avec des types d’enregistrement, mais n’importe quel objet Salesforce peut en avoir. Exemples courants :
- Account — « Client » vs. « Partenaire » vs. « Fournisseur »
- Case — « Support » vs. « Rapport de bug » vs. « Demande de fonctionnalité »
- Lead — « Inbound » vs. « Outbound » vs. « Référence partenaire »
Le même pattern s’applique : si les types d’enregistrement représentent des processus métier fondamentalement différents, divisez. S’ils sont des variations du même processus, gardez-les ensemble avec une colonne de type. Le package dbt_salesforce de Fivetran ne divise pas par type d’enregistrement par défaut — il vous donne un seul modèle d’opportunités. Si votre org a besoin de la division, vous devrez soit personnaliser le package soit construire vos propres modèles de base autour de lui.
Comment trouver vos IDs de types d’enregistrement
Interrogez la table RecordType dans votre entrepôt (Fivetran et d’autres connecteurs l’extraient automatiquement) :
SELECT id AS record_type__id, sobject_type AS record_type__object, name AS record_type__name, is_active AS record_type__is_activeFROM {{ source('salesforce', 'record_type') }}WHERE sobject_type = 'Opportunity'ORDER BY nameCela vous donne le mapping entre IDs et noms lisibles. Exécutez cette requête lors de la configuration de votre projet et à chaque fois que votre admin Salesforce vous notifie de changements.