Aprender principios sobre la Capa de servicios
Objetivos de aprendizaje
Después de completar esta unidad, podrá:
- Explicar los orígenes del patrón Servicio a partir de los patrones Enterprise Application Architecture de Martin Fowler.
- 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.
La 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. En las siguientes secciones, se definen las directrices para crear una implementación de capa de Servicio en Apex, a la vez que se tienen en cuenta las mejores prácticas y los límites reguladores de Salesforce.
¿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 cliente es un código escrito con un controlador Visualforce o un método @AuraEnabled
. 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 todas las formas en que puede invocarse la lógica de Apex en Salesforce Platform.
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, cuando los usuarios interactúan con su aplicación mediante una función determinada expuesta a través de distintas tecnologías de Salesforce. 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íficaInvoiceService.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 principales inquietudes sobre el código en Salesforce 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<TaxCalculation> taxCalculations)
mientras este método fuerza a los interlocutores a llamar al método de forma repetidaInvoiceService.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 modificadorwithout 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 sistemaDatabase.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.
Uso de servicios en Apex
Veamos cómo puede utilizar el método OpportunitiesService.applyDiscounts
desde un número de ubicaciones. A continuación se muestran todos los componentes Lightning y Apex por lotes.
El siguiente ejemplo aborda un único objeto Opportunity (Oportunidad) seleccionado mediante un componente Lightning. Supongamos que tiene un componente Lightning que le solicita al usuario aplicar un porcentaje de descuento en un monto de oportunidad seleccionado. Tenga en cuenta que el manejo de errores se efectúa en esta etapa, no en el servicio, porque los componentes Lightning tienen su propia forma de arrojar errores.
@AuraEnabled public void applyDiscount(Id opportunityId, Decimal discountPercentage) { try { // Apply discount entered to the current Opportunity OpportunitiesService.applyDiscounts( new Set<ID> { opportunityId }, discountPercentage); } catch(Exception e) { throw new AuraHandledException('Something went wrong: ' + e.getMessage()); } }
El siguiente ejemplo trata con fragmentos de procesamiento de registros a través del método de ejecución Apex por lotes. Si observa atentamente, notará que el manejo de excepciones difiere del ejemplo de componente Lightning 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) { } }
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
- Separation of Concerns (Wikipedia)
- Martin Fowler’s Service Layer Pattern
- Martin Fowler’s Enterprise Architecture Patterns