Skip to main content

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)? En esta insignia, las validaciones de los retos prácticos de Trailhead funcionan en inglés. Entre paréntesis se incluyen las traducciones a modo de referencia. En su instancia de Trailhead Playground, asegúrese de (1) cambiar la configuración local a los Estados Unidos, (2) cambiar el idioma a inglés, y (3) copiar y pegar solo los valores en inglés. Siga las instrucciones que figuran aquí.

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

¡Siga aprendiendo gratis!
Regístrese para obtener una cuenta y continuar.
¿Qué hay para usted?
  • Consiga recomendaciones personalizadas para sus objetivos profesionales
  • Practique sus aptitudes con retos prácticos y pruebas
  • Siga y comparta su progreso con empleadores
  • Póngase en contacto para recibir asesoramiento y oportunidades laborales