Skip to main content
Build the future with Agentforce at TDX in San Francisco or on Salesforce+ on March 5–6. Register now.

Aplicar principios de la Capa de servicios en Apex

Objetivos de aprendizaje

Después de completar esta unidad, podrá:

  • Crear una clase de Apex de servicio y hacer un uso efectivo de ella en su aplicación.
  • Exponer una clase de Apex de servicio como una API.
Nota

Nota

¿Es su idioma de aprendizaje español (LATAM)? Comience el reto en un Trailhead Playground en español (LATAM) y utilice las traducciones entre paréntesis para navegar. Copie y pegue solo los valores en inglés, ya que las validaciones del reto dependen de los datos en ese idioma. Si no aprueba el reto en su organización en español (LATAM), recomendamos que (1) cambie la configuración local a Estados Unidos, (2) cambie el idioma a inglés (según estas instrucciones) y, luego, (3) haga clic en el botón “Check Challenge” (Comprobar el reto) nuevamente.

Consulte la insignia Trailhead en su idioma para obtener más información sobre cómo aprovechar la experiencia de Trailhead en otros idiomas.

Siga el proceso con Trail Together

¿Desea seguir el proceso con un experto a medida que realiza este paso? Mire este video que forma parte de la serie Trail Together.

(Este video comienza en el minuto 38:48, en caso de que desee rebobinar y mirar el comienzo del paso nuevamente).

Creación de servicios

¡Adentrémonos en algunos códigos! Si está utilizando una encapsulación completa de la gestión de estados y base de datos, un enfoque de implementación es utilizar una clase denominada apropiadamente con métodos estáticos representando las operaciones del servicio.

Los métodos en su clase representan las operaciones de servicio, que acceden a la información que necesitan mediante el entorno y los parámetros pasados. La lógica en el método actualiza la base de datos o devuelve información sobre el tipo de devolución del método utilizando excepciones de Apex para indicar fallos. El siguiente ejemplo muestra un servicio para aplicar un descuento concreto a un conjunto de Oportunidades (y partidas, si presentes).

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 {}
}

Si aplicamos la consideración de diseño de configuración descrita anteriormente, podemos agregar una versión sobrecargada del servicio anterior con un parámetro Opciones, que permite a la persona que llama instruir el servicio para omitir la asignación del trabajo. La devolución de los valores descontados permite al cliente implementar una vista previa de los descuentos que se podrían aplicar.

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

La firma del método en el ejemplo completo anterior utiliza una lista de Id., que es también por las consideraciones de diseño. Sin embargo, solo se utilizó un parámetro único para el descuento. La presunción es que el mismo descuento se aplicó a todas las oportunidades. Sin embargo, si se requiere la habilitación de diferentes descuentos por oportunidad, puede utilizar una clase de parámetro, como se muestra aquí.

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

Exponer servicios como API

¡A todo el mundo le encantan las API! Exponer su lógica de servicio a partes externas a través de una API en su aplicación es imprescindible para desarrollar un fuerte ecosistema de innovación e integraciones de socios alrededor de sus productos.

Si considera su capa de servicio como probada por completo, robusta y abierta a cualquier cliente (y por qué no lo sería si está utilizándola también, ¿verdad?), la forma más sencilla de exponerla a desarrolladores de Apex es modificar la clase y los modificadores de método de public a global. ¡Bieeen!

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

Sin embargo, nada en la vida es eterno como suena. Si está creando un paquete de AppExchange, el uso de global tiene implicaciones al cambiar firmas de método entre versiones. Asegúrese de entender estas implicaciones.

Merece asimismo la pena considerar la exposición de su API para interlocutores fuera de la plataforma, como mobile o IoT. Una forma de hacer esto es a través del protocolo de REST. Aquí le mostramos una implementación de API de REST de Apex personalizada.

@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);
    }
}

El Id. de la oportunidad está tomado de la URI, y el porcentaje de descuento está tomado de la información publicada. Al igual que con el ejemplo Comunicación remota de JavaScript en la unidad anterior, la gestión de excepciones se deja al interlocutor. La plataforma organiza excepciones en la respuesta XML o JSON apropiada.

Sugerencia avanzada: Considere la posibilidad de exponer algunos métodos invocables para que los usuarios de Flow Builder puedan acceder a su función de capa de servicio sin redactar códigos. ¡No son maravillosas las plataformas!

Recursos

Comparta sus comentarios de Trailhead en la Ayuda de Salesforce.

Nos encantaría saber más sobre su experiencia con Trailhead. Ahora puede acceder al nuevo formulario de comentarios en cualquier momento en el sitio de Ayuda de Salesforce.

Más información Continuar a Compartir comentarios