Skip to main content

Anwenden der Prinzipien der Service-Schicht in Apex

Lernziele

Nachdem Sie diese Lektion abgeschlossen haben, sind Sie in der Lage, die folgenden Aufgaben auszuführen:

  • Erstellen einer Service-Apex-Klasse und effektives Nutzen dieser Klasse in ihrer Anwendung
  • Verfügbarmachen einer Service-Apex-Klasse als API
Hinweis

Hinweis

Lernen Sie auf Deutsch? In diesem Badge ist für die praktischen Trailhead-Aufgaben Englisch als Bearbeitungssprache festgelegt. Übersetzungen werden zur Referenz in Klammern angegeben. Vergewissern Sie sich, dass Sie in Ihrem Trailhead-Playground (1) das Gebietsschema auf USA und (2) die Sprache auf Englisch festgelegt haben. (3) Verwenden Sie zum Kopieren und Einfügen nur die englischen Werte. Die zugehörigen Anweisungen finden Sie hier.

Weitere Details dazu, wie Sie die übersetzte Trailhead-Umgebung optimal nutzen können, finden Sie unter dem Badge "Trailhead in Ihrer Sprache".

Mit Trail Together einem Dozenten folgen

Möchten Sie bei diesem Schritt einem Experten folgen? Schauen Sie sich dieses Video an, das Teil der Reihe "Trail Together" ist.

(Dieser Clip beginnt bei Minute 38:48, für den Fall, dass Sie zurückspringen und den Anfang des Schritts noch einmal sehen möchten.)

Erstellen von Services

Tauchen wir ein in den Code! Wenn Sie eine vollständige Kapselung der Datenbank- und Zustandsverwaltung verwenden, besteht ein Implementierungsansatz darin, eine passend benannte Klasse mit statischen Methoden zu verwenden, die die Vorgänge des Service darstellen.

Die Methoden in Ihrer Klasse stellen die Service-Vorgänge dar, die über die Umgebung und die übergebenen Parameter auf die benötigten Informationen zugreifen. Die Logik in der Methode aktualisiert die Datenbank oder gibt Informationen im Rückgabetyp der Methode zurück, wobei sie benutzerdefinierte Apex-Ausnahmen verwendet, um Fehler zu melden. Das folgende Beispiel zeigt einen Service, der dazu dient, einen bestimmten Rabatt auf eine Gruppe von Opportunities (und Belegposten, sofern vorhanden) anzuwenden.

public with sharing class OpportunitiesService {
   public static void applyDiscounts(Set<Id> opportunityIds, Decimal discountPercentage) {
        // Validate parameters
        if(opportunityIds==null || opportunityIds.size()==0)
            throw new OpportunityServiceException('Opportunities not specified.');
        if(discountPercentage<0 || discountPercentage>100)
            throw new OpportunityServiceException('Invalid discount to apply.');
        // Query Opportunities and Lines (SOQL inlined for this example, see Selector pattern in later module)
        List<Opportunity> opportunities =
            [select Amount, (select UnitPrice from OpportunityLineItems)
             from Opportunity where Id in :opportunityIds];
        // Update Opportunities and Lines (if present)
        List<Opportunity> oppsToUpdate = new List<Opportunity>();
        List<OpportunityLineItem> oppLinesToUpdate = new List<OpportunityLineItem>();
        Decimal factor = 1 - (discountPercentage==null ?0 :discountPercentage / 100);
        for(Opportunity opportunity :opportunities) {
            // Apply to Opportunity Amount
            if(opportunity.OpportunityLineItems!=null && opportunity.OpportunityLineItems.size()>0) {
                for(OpportunityLineItem oppLineItem :opportunity.OpportunityLineItems) {
                    oppLineItem.UnitPrice = oppLineItem.UnitPrice * factor;
                    oppLinesToUpdate.add(oppLineItem);
                }
            } else {
                opportunity.Amount = opportunity.Amount * factor;
                oppsToUpdate.add(opportunity);
            }
        }
        // Update the database
        SavePoint sp = Database.setSavePoint();
        try {
            update oppLinesToUpdate;
            update oppsToUpdate;
        } catch (Exception e) {
            // Rollback
            Database.rollback(sp);
            // Throw exception on to caller
            throw e;
        }       
    }
    public class OpportunityServiceException extends Exception {} 
}

Wenn wir die zuvor beschriebenen Überlegungen zum Konfigurationsdesign anwenden, können wir eine überladene Version des obigen Service mit einem Parameter "Options" hinzufügen, mit dem der Aufrufer den Service anweisen kann, die Festschreibung der Vorgänge zu überspringen. Die Rückgabe der Rabattwerte ermöglicht es dem Client, eine Vorschau der Rabatte zu implementieren, die angewandt würden.

public static List<Decimal> applyDiscounts(
     List<Id> opportunityIds, Decimal discountPercentage, Options config)

Die Methodensignatur im obigen, ungekürzten Beispiel akzeptiert eine Liste von IDs, was ebenfalls den Designüberlegungen entspricht. Es wurde jedoch nur ein einzelner Parameter für den Rabatt verwendet. Dabei wird davon ausgegangen, dass für alle Opportunities der gleiche Rabatt gilt. Wenn jedoch unterschiedliche Rabatte pro Opportunity gewährt werden sollen, können Sie eine Parameterklasse verwenden, wie nachfolgend gezeigt.

public class OpportunityService {
    public class ApplyDiscountInfo {
        public Id OpportunityId;
        public Decimal DiscountPercentage;
    }
    public static void applyDiscounts(List<ApplyDiscountInfo> discInfos) {
      // Implementation...    }
}

Offenlegen von Services als APIs

Alle lieben APIs! Das Offenlegen Ihrer Service-Logik für externe Parteien durch eine API in Ihre Anwendung ist ein Muss, um ein starkes Ökosystem von Innovationen und Partner-Integrationen rund um Ihre Produkte zu entwickeln.

Wenn Ihre Service-Schicht Ihrer Meinung nach vollständig getestet, robust und für jeden Client offen ist (und warum sollte sie dies nicht sein – Sie nutzen sie ja schließlich auch), dann ist der einfachste Weg, sie den Apex-Entwicklern zugänglich zu machen, die Klasse und Methodenmodifikatoren von public in global zu ändern. Tusch!

global class OpportunityService {
    global class ApplyDiscountInfo {
        global Id OpportunityId;
        global Decimal DiscountPercentage;
    }
    global static void applyDiscounts(List<ApplyDiscountInfo> discInfos) {
       // Implementation...    }
}

Das ist allerdings nicht so leicht, wie es sich anhört. Wenn Sie ein AppExchange-Paket erstellen, hat die Verwendung von global bestimmte Auswirkungen beim Ändern von Methodensignaturen zwischen Versionen. Stellen Sie sicher, dass Ihnen diese Auswirkungen klar sind.

Es ist auch eine Überlegung wert, Ihre API für Aufrufer außerhalb der Plattform, wie z. B. Mobilgeräte oder das IoT, zur Verfügung zu stellen. Das eignet sich das REST-Protokoll gut. Hier sehen Sie eine benutzerdefinierte Implementierung einer Apex-REST-API.

@RestResource(urlMapping='/opportunity/*/applydiscount')
global with sharing class OpportunityApplyDiscountResource {
    @HttpPost
    global static void applyDiscount(Decimal discountPercentage) {
        // Parse context
        RestRequest req = RestContext.request;
        String[] uriParts = req.requestURI.split('/');
        Id opportunityId = uriParts[2];
        // Call the service
        OpportunitiesService.applyDiscounts(
            new Set<Id> { opportunityId }, discountPercentage);     
    }
}

Die ID der Opportunity wird aus dem URI ermittelt, und der Rabattprozentsatz wird aus den geposteten Informationen übernommen. Wie beim Beispiel zu JavaScript Remoting in der vorherigen Einheit wird die Ausnahmebehandlung dem Aufrufer überlassen. Die Plattform ordnet Ausnahmen der entsprechenden JSON- oder XML-Antwort zu.

Expertentipp: Sie könnten einige aufrufbare Methoden verfügbar machen, damit Benutzer von Flow Builder auf die Funktionalität Ihrer Service-Schicht zugreifen können, ohne Code zu schreiben. Sind Plattformen nicht einfach überwältigend!

Ressourcen

Lernen Sie weiter kostenlos!
Registrieren Sie sich für einen Account, um fortzufahren.
Was ist für Sie drin?
  • Holen Sie sich personalisierte Empfehlungen für Ihre Karriereplanung
  • Erproben Sie Ihre Fähigkeiten mithilfe praktischer Aufgaben und Quizze
  • Verfolgen Sie Ihre Fortschritte nach und teilen Sie sie mit Arbeitgebern
  • Nutzen Sie Mentoren und Karrierechancen