Skip to main content

Aprender principios sobre la Capa de servicios

Objetivos de aprendizaje

Después de completar esta unidad, podrá:

  • Explicar los orígenes del patrón de Servicio desde patrones de Martin Fowler Enterprise Application Architecture.
  • Determinar qué código de Apex pertenece a la capa Servicio.
  • Debatir acerca de cómo encaja la capa de servicio en su arquitectura de aplicación y la plataforma.
  • Diseñar una capa de Servicio para funcionar en las mejores prácticas de la plataforma.

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 17:45, en caso de que desee rebobinar y mirar el comienzo del paso nuevamente).

Introducción

La unidad anterior presentó SOC como un medio para centrar los arquitectos de software en pensar acerca de la lógica de aplicación de capas. Esta unidad se centra en la definición y la utilización de la capa de Servicio como un punto de entrada clave a otras capas y consumidores (como una API) de su aplicación.

Capa de servicio, “Define el límite de una aplicación con una capa de servicios que establece un conjunto de operaciones disponibles y coordina la respuesta de la aplicación en cada operación”. Martin Fowler / Randy Stafford, EAA Patterns

La capa de Servicio lo ayuda a formar una encapsulación clara y estricta de tareas de negocio de implementación de código, cálculos y procesos. Es importante asegurarse de que la capa de Servicio está lista para su uso en diferentes contextos, como aplicaciones móviles, formularios de interfaz de usuario, interfaces de usuario web enriquecidas y numerosas API. Debe permanecer pura y abstracta para soportar los tiempos cambiantes y las exigencias ante ello. Las siguientes secciones definen las directrices para la creación de una implementación de capa de Servicio en Apex mientras tiene en cuenta mejores prácticas y límites de regulación de Force.com.

¿Quién utiliza la capa de Servicio?

Podría tener la tentación de decir que "todos los chicos modernos" utilizan una capa de servicio, pero técnicamente, el consumidor de una capa de servicio se denomina el "cliente". Un cliente invoca su código de capa de servicio. No existen interacciones humanas con la capa de Servicio, pero otras partes del código que interactúan con el usuario o el sistema, como un controlador de interfaz de usuario o Apex por lotes.

Un ejemplo de un cliente es código redactado en una clase Controlador de Lightning o Visualforce. Sin embargo, existen varios otros clientes (consumidores) de su código de capa de servicio a considerar. Para crear una lista de candidatos, piense en las formas en que se puede invocar la lógica de Apex en la plataforma Force.com.

Formas en que se puede invocar la lógica de Apex en la plataforma Force.com: Controladores de interfaz de usuario de Apex, Servicios Web de Apex, Servicios de REST de Apex, Métodos invocables, Gestores de email entrante, Apex por lotes, Apex programado y para inclusión.

Nota

Nota

Los desencadenadores de Apex faltan porque la lógica pertenece a la capa Dominio de su aplicación, que está estrechamente alineada con los objetos y por tanto la manipulación de los registros en su aplicación. La lógica de Dominio se llama de forma directa o indirecta en la capa de Servicio y, por supuesto, a través de las API y la interfaz de usuario de la plataforma.

Como puede imaginar, es demasiado fácil perder la lógica de capa de servicio en el código de Apex creado para otras capas y fines. Las pérdidas erosionan el valor de implementación de una capa de Servicio porque dan lugar a incoherencias en el filtrado de la experiencia de sus usuarios finales. Por ejemplo, un usuario interactúa con su aplicación a través de una función concreta expuesta en varias de las tecnologías de Force.com. Como un cálculo concreto expuesto a través de un Componente Lightning que creó así como a través de un Servicio de REST de Apex. El comportamiento en ambos casos debe ser coherente. En la siguiente sección, debatimos acerca del diseño y las responsabilidades de la capa de servicio y las expectativas que el código que consume la capa de servicio debe tener.

Adaptabilidad e innovación de plataforma

Las tecnologías enumeradas anteriormente se introdujeron gradualmente como nuevas funciones en la plataforma a lo largo de los años. Imagine si redactó códigos de forma que los enlaza con una función específica y tuvo que reescribir cada vez. Ahora imagine lo fácil que sería adoptar y adaptar su aplicación a estas funciones y las que quedan por venir si no tuviera que preocuparse por reescribir su código desde una de las áreas previas primero. O peor, duplicando el código, porque terne que la reescritura romperá funciones existentes. Buf.

Consideraciones de diseño

  • Convenciones de nomenclatura: La capa de Servicio debe ser lo suficientemente abstracta para ser significativa para número de clientes. Este aspecto a menudo cae en los verbos y sustantivos que utiliza en nombres de clase, métodos y parámetro. Asegúrese de que se expresan en términos generales de la aplicación o tarea en vez de relacionarse con un interlocutor de cliente específico. Por ejemplo, este nombre de método se basa en la operación de negocio InvoiceService.calculateTax(...) mientras que este nombre de método se basa en una operación de uso del cliente específica InvoiceService.handleTaxCodeForACME(...). El segundo nombre de método debería dejarle una sensación de desasosiego.
  • Plataforma / Simpatía de interlocutor: Firmas de método de diseño que admiten las mejores prácticas de la plataforma, especialmente la masificación. Una de las preocupaciones principales de todos los códigos en Force.com es la masificación. Considere servicios que se pueden llamar con listas frente a conjuntos de parámetros únicos. Por ejemplo, los parámetros de este método permiten la masificación InvoiceService.calculateTax(List<TaxCalulation> taxCalulations) mientras este método fuerza a los interlocutores a llamar al método de forma repetida InvoiceService.calculateTax(Invoice invoice, TaxInfo taxCodeInfo). De nuevo, el segundo debería dejarle una sensación de desasosiego.
  • Consideraciones de SOC: El código de capa de servicio encapsula lógica de tarea y proceso utilizando habitualmente múltiples objetos en su aplicación. Piense en esto como un orquestador. En cambio, el código relacionado específicamente con validación, valores de campo o cálculos, que se producen durante la inserción, las actualizaciones y las eliminaciones de registros es el problema del objeto relacionado. Dicho código se redacta habitualmente en desencadenadores de Apex y puede permanecer allí. No se preocupe, introduciremos el patrón Dominio para este tipo de código en breve.
  • Seguridad: El código de capa Seguridad y el código que llama se deben ejecutar de forma predeterminada con seguridad de usuario aplicada. Para garantizar que este sea el caso, utilice el modificador with sharing en sus clases de Servicio de Apex (especialmente importante si está exponiendo dicho código vía el modificador global). Si la lógica de Apex debe acceder a registros fuera de la visibilidad del usuario, el código debe elevar de forma explícita el contexto de ejecución en la mayor brevedad posible. Un buen enfoque es usar una clase interna de Apex privada con el modificador without sharing aplicado.
  • Clasificación: Evite prescribir cómo se gestionan aspectos de interacción con la capa de servicio porque es mejor dejar algunos aspectos a los interlocutores de su servicio, por ejemplo, semánticas como mensajería y gestión de errores. Los interlocutores tienen a menudo sus propios medios para interpretar y gestionar estos. Por ejemplo, Visualforce utiliza <apex:pagemessages>, y Programar trabajos utilizará probablemente emails, publicaciones de Chatter o registros para comunicar errores. En este caso, es habitualmente mejor aprovechar las semánticas de gestión de errores predeterminadas de Apex lanzando excepciones. De forma alternativa, su servicio puede proporcionar comentarios de actualizaciones de base de datos parciales al interlocutor. En este caso, elabore la clase de Apex apropiada y devuelva una lista de ese tipo. El método del sistema Database.insert es un buen ejemplo de este tipo de firma de método.
  • Servicios compuestos: Aunque los clientes pueden ejecutar múltiples llamadas de servicio una tras otra, hacerlo puede ser ineficiente y causar problemas de transacciones de la base de datos. Es mejor crear servicios compuestos que agrupan de forma interna múltiples llamadas de servicio en una sola llamada de servicio. Es también importante asegurarse de que la capa de servicio está lo más optimizada posible con respecto al uso de SOQL y DML. Esto no significa que no se pueden exponer más servicios precisos; solo significa que debe ofrecer a los interlocutores la opción de utilizar un servicio único específico si es necesario.
  • Falta de estado y gestión de transacciones: Los clientes de la capa de servicio a menudo tienen diferentes requisitos acerca de la durabilidad del proceso que se está emprendiendo y la información que se está gestionando. Por ejemplo, una solicitud única al servidor y múltiples solicitudes se dividen en ámbitos separados: el estado de gestión (como Apex por lotes) o una interfaz de usuario compleja que mantiene su propio estado de página entre varias solicitudes. Dadas estas variaciones en la gestión de estados, es mejor encapsular operaciones de base de datos y estado de servicio en la llamada del método a la capa de servicio. En otras palabras, haga que el servicio sea sin estado para proporcionar a los contextos de llamada la flexibilidad para implementar sus propias soluciones de gestión de estado. El ámbito de una transacción con la base de datos debe también estar incluido en cada método de servicio de modo que el interlocutor no tenga que considerar esto con su propio SavePoints, por ejemplo.
  • Configuración: Es posible que tenga una configuración común o sustituciones de comportamiento en una capa de servicio, como proporcionar control para permitir al cliente indicar a la capa de servicio no realizar cambios o enviar mensajes de email. Este escenario podría ser útil en casos donde el cliente está implementando funciones de tipo vista previa o qué sucedería. Asegúrese de considerar cómo implementa esta coherencia, quizás como una sobrecarga de método que toma un parámetro Opciones compartido, similar a los métodos DML en Apex.
Nota

Nota

En Apex, las transacciones de base de datos se comprometen automáticamente si la solicitud se completa sin errores y se revierten en el evento de una excepción sin gestionar. Sin embargo, permitir que se complete una solicitud con errores proyectados no es una experiencia de usuario deseada porque la gestión de plataforma de estas excepciones a menudo no es accesible (Trabajos de Apex por lotes) o estéticamente agradable (página en blanco, texto negro) para usuarios finales. Por este motivo, los desarrolladores a menudo capturan excepciones y las dirigen en consecuencia. Un posible efecto lateral con este enfoque es que la plataforma ve que existe una realización válida de la solicitud y asigna registros que se insertaron o se actualizaron conduciendo al error que se produjo. Siguiendo los principios de diseño de capa de servicio anterior acerca de la falta de estado y la gestión de transacciones, puede evitar este problema.

Uso de servicios en Apex

Observemos algunos códigos. Imagine que tiene un botón personalizado en el formato Oportunidad que cuando se pulsa muestra una página de Visualforce que solicita al usuario aplicar un porcentaje de descuento al Importe de oportunidad o, si está presente, las Partidas de oportunidad asociadas.

Veamos cómo puede utilizar el método OpportunitiesService.applyDiscounts desde un número de ubicaciones. Visualforce, Apex por lotes y Comunicación remota de JavaScript se muestran a continuación. El siguiente ejemplo trata con una Oportunidad única seleccionada a través de un StandardController. Observe que la gestión de errores del controlador se realiza por el controlador, no el servicio, porque Visualforce tiene su propia forma de extraer errores.

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

El siguiente ejemplo trata con múltiples Oportunidades seleccionada a través de 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();               
}

El siguiente ejemplo trata con fragmentos de procesamiento de registros a través del método de ejecución Apex por lotes. Si examina atentamente observará que la gestión de excepciones es diferente del ejemplo de controlador de Visualforce anterior.

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

Este ejemplo ajusta el método de servicio o lo expone a través de Comunicación remota de JavaScript. Aquí, las excepciones no se capturan, porque Comunicación remota de JavaScript tiene la clasificación integrada de excepciones cuando se proyectan. Deseamos aprovechar esto, pasándolos en el código de cliente de JavaScript para detectar utilizando las instalaciones integradas.

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

En una unidad posterior, echaremos un vistazo a la exposición de un método de Servicio a través de una API de REST.

Otras ventajas y consideraciones de una capa de Servicio de Apex

Fuera del ámbito de esta unidad concreta está el tema de servicios de implementación para pruebas simuladas y desarrollo paralelo. Los servicios pueden utilizar el patrón de generador junto con interfaces de Apex para resolver de forma dinámica la implementación en vez de codificarla directamente en los métodos. Este enfoque es útil para proporcionar más flexibilidad en la ingeniería del ámbito de pruebas en servicios. Sin embargo, los generadores requieren cierta instalación y marco de trabajo para crear interfaces, formas de registrar clases y trabajo divertido en su código. Tenga la seguridad de que su uso agrega valor en términos de simulación y flexibilidad de tiempo de ejecución basándose en configuraciones.

Además, la definición del diseño de capa de servicio por adelantado también permite a los desarrolladores o equipos de desarrolladores trabajar mejor juntos o en paralelo. Aquellos que necesiten llamar los servicios pueden utilizar implementaciones ficticias para devolver datos estáticos, mientras que aquellos que implementan los servicios pueden trabajar en el código sin afectar a sus interlocutores. A menudo se hace referencia a este estilo de desarrollo como Diseñar por contrato (Dbc) y es maravilloso.

Resumen

Invertir en una capa de servicio para su aplicación ofrece las ventajas de ingeniería de mejor reutilización y capacidad de adaptación, sí como proporciona una forma más limpia y más asequible de implementar una API para su aplicación, un imprescindible en el mundo integrado en la nube de la actualidad. Observando de cerca las consideraciones de encapsulación y diseño descritas anteriormente, comienza a formar un núcleo duradero para su aplicación que durará y permanecerá siendo una inversión sólida en los tiempos cambiantes e innovadores.

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