Skip to main content

Apprendre les principes de la couche Service

Objectifs de formation

Une fois cette unité terminée, vous pourrez :

  • Expliquer les origines du modèle Service des modèles d'architecture d'application d'entreprise de Martin Fowler.
  • Déterminer quel code Apex appartient à la couche Service.
  • Expliquer la place qu’occupe la couche Service dans votre architecture d’application et la plate-forme.
  • Concevoir une couche Service qui fonctionne avec les bonnes pratiques de la plate-forme.

Vidéo de démonstration Trail Together

Vous souhaitez être guidé pas à pas par un expert pendant que vous travaillez sur cette étape ? Regardez cette vidéo qui fait partie de la série Trail Together.

(Ce clip commence à 17 min 45 s, au cas où vous voudriez revenir en arrière et regarder à nouveau le début de l’étape.)

Introduction

L'unité précédente a présenté la SOC comme un moyen d’inciter les architectes logiciels à envisager la logique applicative en plusieurs couches. Cette unité se concentre sur la définition et l'utilisation de la couche Service comme un point d'entrée clé vers d'autres couches et consommateurs (par ex., une API) de votre application.

La Couche Service « définit les limites d’une application par une couche de services qui établit un ensemble d’opérations disponibles et coordonne la réponse de l’application dans chaque opération ». Martin Fowler / Randy Stafford, EAA Patterns

La couche Service vous aide à former une encapsulation claire et stricte du code qui met en œuvre des tâches métier, des calculs et des processus. Il est important de s'assurer que la couche Service est utilisable dans différents contextes : applications mobiles, formulaires d'IU, IU Web riches et nombreuses API. Elle doit rester pure et abstraite pour supporter l'évolution des temps et des demandes à venir. Les sections suivantes définissent les recommandations pour la création de l’implémentation d'une couche Service dans Apex tout en gardant à l'esprit les bonnes pratiques de Force.com et les limitations du gouverneur.

Qui utilise la couche Service ?

Vous pourriez être tenté de dire que « tous les développeurs dans le coup » utilisent une couche de service, mais techniquement, le consommateur d'une couche de service est appelé le « client ». Un client appelle votre code de couche de service. Il ne s'agit pas d'interactions humaines avec la couche Service, mais d'autres morceaux de code qui interagissent avec l'utilisateur ou le système, par exemple un contrôleur d'IU ou Apex par lot.

Un client peut être du code écrit dans une classe de contrôleur Visualforce ou Lightning. Toutefois, vous devez prendre en compte de nombreux autres clients (consommateurs) de votre code de couches de services. Pour développer une liste de candidats, pensez à toutes les manières dont peut être invoquée la logique Apex sur la plate-forme Force.com.

Modes d’invocation de la logique Apex sur la plate-forme Force.com : Contrôleurs d’IU Apex, services Web Apex, services REST Apex, méthodes invocables, gestionnaires d’e-mails entrants, Apex par lot, Apex planifié et pour file d’attente.

Remarque

Remarque

Les déclencheurs Apex sont absents car la logique appartient à la couche Domaine de votre application, qui est étroitement alignée sur les objets et donc la manipulation des enregistrements dans votre application. La logique Domaine est appelé à la fois directement et indirectement dans la couche Service et, bien sûr, via l’IU et les API de la plate-forme.

Comme vous pouvez l'imaginer, il est bien trop facile de faire fuiter la logique de la couche de services dans le code Apex créé pour d'autres couches et objectifs. Cette fuite érode la valeur de l’implémentation d'une couche Service, car elle donne naissance à des incohérences qui s'infiltrent jusqu'à l'expérience de vos utilisateurs finaux. Par exemple, lorsque les utilisateurs interagissent avec votre application via une fonctionnalité donnée exposée par l'intermédiaire de plusieurs technologies Force.com. Ou un certain calcul est exposé via un composant Lightning que vous avez développé ainsi que via un service Rest Apex. Dans les deux cas, le comportement doit être cohérent. Dans la section suivante, nous allons aborder la conception et les responsabilités de la couche de services et les attentes d'un code qui consomme la couche de services.

Innovation et adaptabilité de la plate-forme

Au fil des ans, les technologies répertoriées ci-dessus ont été graduellement présentées comme de de nouvelles fonctionnalités sur la plate-forme. Imaginez si vous aviez écrit du code exclusivement associé à une fonctionnalité particulière et qu’il faille le refactoriser à chaque fois. Maintenant, imaginez combien il serait plus facile d'adopter et d'adapter votre application à ces fonctionnalités et à celles à venir si vous n'aviez pas à vous soucier d'abord de la refactorisation de votre code à partir de l'une des zones précédentes. Ou, pire, à dupliquer votre code de crainte que la refactorisation ne casse la fonctionnalité existante. Bigre.

Considérations de conception

  • Conventions de nommage : la couche Service doit être suffisamment abstraite pour être significative pour un grand nombre de clients. Cet aspect incombe souvent aux verbes et aux noms que vous utilisez dans la classe, les méthodes et les noms de paramètres. Assurez-vous qu'ils sont exprimés en termes généraux de l'application ou de la tâche plutôt qu'en rapport avec un appelant client spécifique. Par exemple, ce nom de méthode est basé sur une activité d’entreprise InvoiceService.calculateTax(...) alors que ce nom de méthode est basé sur une opération d’utilisation de client spécifique InvoiceService.handleTaxCodeForACME(...). Le nom de la seconde méthode devrait vous gêner un tant soit peu.
  • Sympathie plate-forme/appelant : signatures de méthodes de conception qui prennent en charge les bonnes pratiques de la plate-forme, en particulier le traitement en masse. L’une des principales préoccupations concernant les codes sur Force.com est le traitement en masse. Envisagez des services qui puissent être appelés avec des listes plutôt qu'avec des ensembles de paramètres uniques. Par exemple, les paramètres de cette méthode permettent un traitement en masse InvoiceService.calculateTax(List<TaxCalculation> taxCalculations) alors que cette méthode force les appelants à appeler la méthode à plusieurs reprises InvoiceService.calculateTax(Invoice invoice, TaxInfo taxCodeInfo). Une fois encore, la seconde doit vous mettre un peu mal à l'aise.
  • Considérations SOC : le code de la couche Service encapsule la logique de tâche ou de traitement, généralement en utilisant plusieurs objets dans votre application. Voyez cela comme un orchestrateur. Par opposition, le code spécifiquement relatif à une validation, à des valeurs de champ ou à des calculs, qui se produisent pendant des insertions, des mises à jour et des suppressions d'enregistrement, constitue la préoccupation de l'objet associé. Ce code est généralement écrit dans des déclencheurs Apex et peut y demeurer. Ne vous inquiétez pas, nous allons bientôt présenter le modèle Domaine pour ce type de code.
  • Sécurité : le code de la couche Service et le code qu'il appelle doivent par défaut fonctionner avec la sécurité utilisateur appliquée. Pour vous assurer que tel est le cas, utilisez le modificateur with sharing dans vos classes Service Apex (ceci est particulièrement important si vous exposez ce code via le modificateur global). Si la logique APEC doit accéder à des enregistrements hors de visibilité de l'utilisateur, le code doit explicitement augmenter le contexte d'exécution aussi brièvement possible. Une bonne approche consiste à utiliser une classe interne Apex privée avec le modificateur without sharing appliqué.
  • Conversion de paramètres : évitez de prescrire comment les aspects de l'interaction avec la couche de services sont gérés, car il est préférable de laisser certains aspects aux appelants de votre service, par exemple, la sémantique telle que la gestion des erreurs ou la messagerie. Les appelants disposent souvent de leurs propres moyens pour les interpréter et les gérer. Par exemple, Visualforce utilise <apex:pagemessages> et les tâches de planification utiliseront probablement des e-mails, des publications Chatter ou des journaux pour communiquer les erreurs. De sorte que dans ce cas, il est généralement préférable d'utiliser la sémantique par défaut de gestion des erreurs d'Apex en émettant des exceptions. Votre service peut également fournir un retour de mise à jour partiel de base de données à l'appelant. Dans ce cas, concevez une classe Apex appropriée et renvoyez une liste de ce type. La méthode Database.insert du système est un bon exemple de ce type de signature de méthode.
  • Services composés : bien que des clients puissent exécuter plusieurs appels de service l'un après l'autre, cela peut s'avérer inefficace et provoquer des problèmes transactionnels de base de données. Il est préférable de créer des services composés qui regroupent en interne plusieurs appels de services en un seul. Il est également important de s'assurer que la couche de services est aussi optimisée que possible en ce qui concerne l'utilisation de SOQL et DML. Cela ne signifie pas que davantage de services granulaires ne puissent pas être exposés ; mais seulement que vous devez donner aux appelants la possibilité d'utiliser un service unique plus spécifique si nécessaire
  • Gestion des transactions et absences d'état : les clients de la couche de services ont souvent différentes exigences en ce qui concerne la longévité du processus entrepris et des informations gérées. Par exemple, une requête unique sur un serveur et plusieurs requêtes divisées en portées distinctes : l'état de gestion (par exemple Apex par lot) ou une IU complexe qui maintient son propre état de page sur plusieurs demandes. Étant données ces variations sur la gestion de l'état, il est préférable d'encapsuler les opérations de la base de données et l'état de service au sein de l'appel de méthode dans la couche de services. En d'autres termes, faites en sorte que le service soit sans état pour fournir au contexte d'appel la flexibilité d'employer ses propres solutions de gestion d'état. La portée d’une transaction au sein de la base de données doit également être contenue dans chaque méthode de services afin que l’appelant n’ait pas à envisager cela avec son propre SavePoints par exemple.
  • Configuration : vous pouvez avoir une configuration ou des remplacements de comportement communs dans couche de service, par exemple pour fournir un contrôle qui permet aux clients d'ordonner à la couche de serveur de ne pas valider de modification ni envoyer d'e-mail. Ce scénario peut s'avérer utile dans des cas où le client met en œuvre une fonctionnalité d'aperçu ou de type simulation. Veillez à envisager comment vous mettez en œuvre cette cohérence, par exemple en tant que surcharge de méthode qui prend un paramètre Options partagé, identique aux méthodes DML dans Apex.
Remarque

Remarque

Dans Apex, les transactions de bases de données sont automatiquement exécutées si la demande s’effectue sans erreur et annulées dans le cas d’une exception non gérée. Toutefois, autoriser une demande à s’exécuter avec des erreurs n’est pas une expérience utilisateur souhaitable car la plate-forme qui gère ces exceptions est souvent peu accessible à ce point (tâches Apex par lot) et peu esthétique (page blanche, texte noir) pour les utilisateurs finaux. Pour cette raison, les développeurs interceptent les exceptions et les acheminent de manière appropriée. Un effet secondaire potentiel de cette approche réside dans le fait que la plate-forme considère cela comme une exécution valide de la demande et valide les enregistrements qui ont été insérés ou mis à jour, provoquant l’erreur survenue précédemment En suivant les principes de conception de couches de services ci-dessus concernant l’absence d’état et de la gestion des transactions, vous pouvez éviter ce problème.

Utilisation des services dans Apex

Examinons un peu de code. Imaginez que vous ayez un bouton personnalisé dans la présentation de l'opportunité et qui, lorsque vous appuyez dessus, affiche une page Visualforce qui informe l'utilisateur d'un pourcentage de remise à appliquer à la somme de l'opportunité ou, le cas échéant, aux éléments de la ligne d'opportunité associée.

Voyons comment vous pouvez utiliser la méthode OpportunitiesService.applyDiscounts depuis un certain nombre d’endroits. Visualforce, Apex par lot et JavaScript Remoting sont tous indiqués ci-dessous. L'exemple suivant se rapporte à une opportunité unique sélectionnée via un StandardController. Veuillez noter que la gestion d'erreur du contrôleur s'effectue par l'intermédiaire du contrôleur et non par le service, car Visualforce possède sa propre méthode de gestion des erreurs.

public PageReference applyDiscount() {
    try {
        // Apply discount entered to the current Opportunity
        OpportunitiesService.applyDiscounts(
            new Set<ID> { standardController.getId() }, DiscountPercentage);
    } catch (Exception e) {
        ApexPages.addMessages(e);
    }          
    return ApexPages.hasMessages() ?null :standardController.view();
}

L'exemple suivant gère plusieurs opportunités via un StandardSetController.

public PageReference applyDiscounts() {
    try {
        // Apply discount entered to the selected Opportunities
        OpportunitiesService.applyDiscounts(
           // Tip:Creating a Map from an SObject list gives easy access to the Ids (keys)
           new Map<Id,SObject>(standardSetController.getSelected()).keyValues(),
           DiscountPercentage
        );
    } catch (Exception e) {
        ApexPages.addMessages(e);
    }          
    return ApexPages.hasMessages() ?null :standardController.view();               
}

L'exemple suivant se rapporte au traitement de plusieurs segments d'enregistrement via la méthode d'exécution Apex par lot. Si vous examinez cela de près, vous remarquerez que la gestion des exceptions est différente de celle du précédent exemple de contrôleur Visualforce.

public with sharing class OpportunityApplyDiscountJob implements Database.Batchable<SObject> {
    public Decimal DiscountPercentage {get;private set;}
    public OpportunityApplyDiscountJob(Decimal discountPercentage) {
        // Discount to apply in this job        
        this.DiscountPercentage = discountPercentage;
    }
    public Database.QueryLocator start(Database.BatchableContext ctx) {
        // Opportunities to discount
        return Database.getQueryLocator(
            'select Id from Opportunity where StageName = \'Negotiation/Review\'');  
    }
    public void execute(Database.BatchableContext BC, List<sObject> scope) {        
        try {
          // Call the service           
          OpportunitiesService.applyDiscounts(
            new Map<Id,SObject>(scope).keySet(),DiscountPercentage);
        } catch (Exception e) {
            // Email error, log error, chatter error etc..        }
    }
    public void finish(Database.BatchableContext ctx) { }    
}

Cet exemple enveloppe la méthode de service et l'expose via JavaScript Remoting. Ici, les exceptions ne sont pas interceptées, car JavaScript Remoting intègre un tri des exceptions lorsqu'elles sont émises. Nous souhaitons utiliser cela, et les transmettre dans le code client JavaScript pour intercepter l'utilisation des services intégrés.

public class OpportunityController {
    @RemoteAction
    public static void applyDiscount(Id opportunityId, Decimal discountPercent) {
        // Call service
        OpportunitiesService.applyDiscounts(new Set<ID> { opportunityId }, discountPercent);
    }
}

Dans une unité ultérieure, nous examinerons l'exposition de la méthode Service via une API REST.

Autres avantages et considérations d’une couche Service Apex

Hors du cadre de cette unité particulière se trouve la rubrique de mise en œuvre des services pour les tests fictifs et le développement parallèle. Les services peuvent utiliser le modèle d'usine avec les interfaces Apex pour résoudre dynamiquement la mise en œuvre au lieu de la coder directement dans les méthodes. Cette approche est pratique pour fournir davantage de flexibilité au niveau de la conception de la portée des tests sur les services. Toutefois, les usines ont besoin de plomberie et d'un cadre de travail pour créer des interfaces, des manières d'enregistrer des classes et d'autres choses amusantes dans votre code. Soyez assuré que leur utilisation ajoute de la valeur en termes de flexibilité de tests et d'exécution en fonction des configurations.

En outre, la définition de la conception de la couche de services en amont permet également aux développeurs et aux équipes de développeurs de mieux travailler ensemble ou en parallèle. Ceux qui ont besoin d'appeler les services peuvent utiliser les implémentations fictives pour envoyer des données statiques, tandis que ceux qui mettent en œuvre les services peuvent travailler sur le code sans incidence sur leurs appelants. Ce style de développement est souvent appelé Programmation par contrat (Dbc) et c'est une chose magnifique.

Récapitulatif

L'investissement dans une couche de services pour votre application offre les avantages d'ingénierie d'une plus grande réutilisation et d'une plus grande adaptabilité, et fournit également une manière plus propre et plus rentable de mettre en œuvre une API pour votre application, chose indispensable dans l'univers actuel intégré au cloud. En observant étroitement les considérations d'encapsulation et de conception décrites ci-dessus, vous commencerez à créer un noyau durable pour votre application qui persistera et demeurera un investissement robuste tout au long des périodes d'évolution et d'innovation incessantes qui s'annoncent.

Ressources

Formez-vous gratuitement !
Créez un compte pour continuer.
Qu’est-ce que vous y gagnez ?
  • Obtenez des recommandations personnalisées pour vos objectifs de carrière
  • Mettez en pratique vos compétences grâce à des défis pratiques et à des questionnaires
  • Suivez et partagez vos progrès avec des employeurs
  • Découvrez des opportunités de mentorat et de carrière