¿Le resultó útil esta página?
Sus comentarios sobre este contenido son muy importantes. Háganos saber su opinión.
¿Tiene comentarios adicionales?
Caracteres restantes: 1500
Exportar (0) Imprimir
Expandir todo
Expandir Minimizar

Principios de diseño de servicios: patrones y antipatrones de servicios

Agosto de 2005

Publicado: 12 de Diciembre de 2005

John Evdemon
Microsoft Corporation, Architecture Strategy

Este artículo se aplica a:
Arquitectura empresarial
Arquitectura orientada a servicios
Servicios Web

Resumen: la serie de artículos "Principios de diseño de servicios" tiene como objetivo dar a conocer las prácticas de diseño más adecuadas, así como proporcionar códigos de ejemplo cuando sea necesario. En el primero de esta serie de artículos se describen los principios fundamentales relativos al diseño y la implementación de servicios Web, se incluye un breve resumen sobre los conceptos de la arquitectura orientada a servicios (SOA) y se analizan varios de los patrones y antipatrones que los desarrolladores pueden utilizar para crear servicios Web. La información que se ofrece en este artículo es válida para cualquier lenguaje de programación o plataforma para los que se puedan desarrollar e implementar los servicios Web. (18 páginas impresas.)

Artículo inspirado en una transmisión Web realizada por Ron Jacobs, Microsoft Patterns & Practices

Nuestro agradecimiento a Ron Jacobs del equipo Patterns and Practices por sus numerosas ideas y conceptos, los cuales han facilitado la elaboración de este artículo. A finales de 2004, Ron y yo realizamos una transmisión Web basada en este contenido. La elaboración de este artículo no hubiera sido posible sin la ayuda de Ron

Para obtener más información sobre el trabajo que Ron realiza en el equipo Patterns and Practices, consulte http://www.microsoft.com/resources/practices/default.mspx (en inglés).

Descargas para este artículo:

Principios de diseño de servicios: patrón centrado en documentos (en inglés).

Principios de diseño de servicios: patrón inalterable (en inglés).

Principios de diseño de servicios: patrón de reserva (en inglés).

En esta página

Acerca de la serie de principios de diseño de SOA Acerca de la serie de principios de diseño de SOA
Introducción a la arquitectura orientada a servicios (SOA) Introducción a la arquitectura orientada a servicios (SOA)
Patrones y antipatrones para SOA Patrones y antipatrones para SOA
Conclusión Conclusión

Acerca de la serie de principios de diseño de SOA

Este es el primero de una serie de artículos relativos al diseño de servicios Web más eficaces. Aunque este artículo se ha elaborado en colaboración con el equipo patterns & practices de Microsoft, puede que los artículos que se publiquen en el futuro sean el fruto de colaboraciones con otros equipos e individuos, tanto pertenecientes como ajenos a Microsoft.

El lector debe ser cauto a la hora de utilizar el código de ejemplo desarrollado para la serie de principios de diseño de SOA. Este código es únicamente ilustrativo. Se anima al lector a que revise, compile y aprenda del código. Asimismo, el lector deberá evitar hacer uso de cualquier fragmento del código de ejemplo en un entorno de producción. MICROSOFT NO SE RESPONSABILIZA DE LOS DAÑOS QUE SE PUEDAN PRODUCIR EN CASO DE QUE EL LECTOR DECIDA UTILIZAR CUALQUIER FRAGMENTO DEL CÓDIGO DE EJEMPLO EN UN ENTORNO DE PRODUCCIÓN.

Requisitos del sistema para el código de ejemplo:

  • Microsoft Windows XP (los ejemplos no se han probado en otras plataformas)

  • Microsoft .NET Framework 1.1

  • Microsoft Internet Information Server 6.0

  • Microsoft SQL Server o Microsoft Access

  • No se requiere Microsoft Visual Studio 2003, pero su presencia mejorará el conjunto de la experiencia

Aunque Microsoft no admite el código de ejemplo, los autores del artículo animan al lector a que envíe sus comentarios al respecto.

Nota

Dada la rápida evolución de la arquitectura SOA, los artículos y el código de ejemplo de esta serie de artículos estarán sometidos a revisión al tiempo que el espacio crece y mejora.

Introducción a la arquitectura orientada a servicios (SOA)

SOA se ha convertido en un acrónimo ampliamente conocido que suscita diferentes interpretaciones. Si se le pide a dos personas diferentes que definan SOA, probablemente se obtengan dos respuestas muy distintas, y posiblemente contradictorias. Algunos definen la arquitectura SOA como una infraestructura de TI para la habilitación empresarial, mientras que otros la utilizan para aumentar la eficacia de los recursos de TI. En muchos sentidos, la arquitectura SOA se asemeja al poema de John Godfrey Saxe sobre los hombres ciegos y el elefante. Cada hombre describe al elefante de forma ligeramente diferente, ya que cada uno cuenta con su propia experiencia personal (por ejemplo, el hombre que toca la trompa cree que se trata de una serpiente, mientras que el que toca un colmillo cree encontrarse frente a una lanza). En este caso, al tratarse de una entidad física, el elefante de Saxe resulta bastante más sencillo de describir. No obstante, la descripción de la arquitectura SOA resulta más compleja, ya que las filosofías de diseño no están disponibles como manifestaciones físicas.

El concepto SOA hace referencia a un enfoque de arquitectura cuyo objetivo es la creación de sistemas a partir de servicios autónomos. Con SOA, la integración pasa a ser una reflexión previa más que una idea posterior. Probablemente, la solución final esté formada por servicios desarrollados en distintos lenguajes de programación y se aloje en plataformas diferentes con numerosos modelos de seguridad y procesos empresariales. Aunque este concepto pueda parecer altamente complejo, en realidad no se trata de una idea nueva. Ciertos sectores afirman que SOA fue el resultado de las experiencias asociadas al diseño y el desarrollo de sistemas distribuidos basados en tecnologías previamente disponibles. Gran parte de los conceptos asociados a SOA, como los servicios, el descubrimiento y el enlace en tiempo de ejecución, ya estaban asociados a las tecnologías CORBA y DCOM. Asimismo, buena parte de los principios de diseño de servicios cuentan con numerosos aspectos en común con las técnicas anteriores OOA/OOD, basadas en la encapsulación, la abstracción y las interfaces claramente definidas.

Del acrónimo SOA se deriva una pregunta obvia: ¿qué es exactamente un servicio? Un servicio es simplemente un programa con el que se puede interactuar a través de intercambios de mensajes bien definidos. A la hora de diseñar un servicio, es necesario tener en cuenta los conceptos de disponibilidad y estabilidad. Mientras que los servicios se crean para que duren el mayor tiempo posible, las configuraciones y agregaciones de los servicios se elaboran para que puedan cambiar fácilmente. La agilidad suele ser considerada como una de las mayores ventajas de SOA. Una organización que disponga de procesos empresariales implementados en una infraestructura de acoplamiento flexible está mucho más dispuesta al cambio que otra que esté limitada por una serie de aplicaciones monolíticas subyacentes que requieran varias semanas para implementar el más mínimo cambio. Los sistemas de acoplamiento flexible dan lugar a procesos empresariales de acoplamiento flexible, ya que éstos dejan de estar limitados por la infraestructura subyacente. Los servicios y las interfaces asociadas deben permanecer estables, de modo que puedan volver a configurarse o agregarse para adaptarse al continuo cambio al que se ven sometidas las necesidades empresariales. Los servicios permanecen estables al hacer uso de interfaces basadas en estándares y mensajes bien definidos, es decir, esquemas SOAP y XML para la definición de mensajes. Los servicios diseñados para realizar funciones granulares sencillas con conocimiento limitado acerca del modo en que los mensajes se pasan o recuperan tienen más posibilidades de ser reutilizados en una infraestructura SOA de mayor tamaño. Como se mencionó anteriormente, el uso de los principios básicos de diseño orientado a objetos relativos a la encapsulación y el diseño de interfaces nos servirá para diseñar y crear servicios Web reutilizables. Para aplicar los principios de diseño orientado a objetos al campo de los servicios Web, es necesario comprender los "cuatro principios" de la orientación a servicios tan frecuentemente citados.

Principio 1: los límites son explícitos

Los servicios interactúan a través de límites explícitos de paso de mensajes bien definidos. Pasar los límites de los servicios puede resultar costoso, en función de los factores geográficos, de confianza o ejecución. Un límite representa el paso entre la interfaz pública del servicio y su implementación interna y privada. El límite de un servicio se publica a través de WSDL y puede incluir aserciones que indican las expectativas de un servicio dado. Pasar los límites de un servicio es una tarea costosa por varias razones. A continuación se muestran algunas de ellas:

  • La ubicación física del servicio de destino puede ser un factor desconocido.

  • Es probable que los modelos de seguridad y confianza cambien cada vez que se pasen los límites del servicio.

  • La activación y conversión de datos entre las representaciones públicas y privadas de un servicio determinado puede que requiera el uso de recursos adicionales, algunos de los cuales pueden ser externos al propio servicio.

  • Mientras que los servicios se crean para durar el mayor tiempo posible, las configuraciones de servicio se elaboran para poder ser sometidas a cambios. Este hecho implica que el rendimiento de un servicio confiable puede, de repente, verse deteriorado como consecuencia de reconfiguraciones de red o migraciones a otras ubicaciones físicas.

  • Normalmente, los usuarios de servicios no saben cómo se implementan los procesos privados e internos. El usuario de un servicio determinado dispone de control limitado sobre el rendimiento de dicho servicio.

El patrón de integración orientada a servicios (en inglés) indica que "las invocaciones de servicio están sujetas a la latencia de la red, los errores de ésta y los errores de sistema distribuido, aunque no así las implementaciones locales. Es necesario escribir una gran cantidad de lógica de detección y corrección de errores con el fin de anticipar los efectos del uso de interfaces de objetos remotos". Aunque se debe asumir que pasar los límites de un servicio es un proceso costoso, se debe igualmente ser cauto a la hora de implementar métodos locales diseñados para minimizar el paso de límites. Puede que un sistema que implemente métodos y objetos locales monolíticos obtenga un mayor nivel de rendimiento, pero duplicará la funcionalidad de un servicio definido previamente (en la programación orientada a objetos, esta técnica era conocida como "cortar y pegar", y presenta los mismos riesgos relativos al control de versiones del servicio).

Hay varios principios que es preciso tener en cuenta con respecto al primer principio de la integración orientada a objetos:

  • Conocer los límites. Los servicios proporcionan un contrato para definir las interfaces públicas que ofrece. Toda la interactuación con el servicio tiene lugar a través de la interfaz pública. La interfaz consta de procesos públicos y representaciones de datos públicos. El proceso público es el punto de entrada al servicio, mientras que la representación de datos públicos representa los mensajes utilizados por el proceso. Si se utiliza WSDL para representar un contrato simple, el elemento <message> representará los datos públicos y <portType> los procesos públicos. El artículo "Datos externos frente a datos internos" (en inglés) analiza estos problemas en detalle.

  • El consumo de los servicios debe resultar fácil. Al diseñar un servicio, los desarrolladores deben procurar que su consumo resulte sencillo para otros desarrolladores. La interfaz (contrato) del servicio se debe diseñar también de modo que su evolución no implique la ruptura de contratos con los usuarios existentes. (Este tema se tratará con más detalle en próximos artículos de la serie.)

  • Evitar interfaces RPC. Pasar mensajes de forma explícita constituye una técnica más adecuada que el uso de modelos RPC. Este enfoque aísla al usuario del mecanismo interno de la implementación del servicio, lo que evita que los desarrolladores de servicios deban, además de desarrollar los servicios, minimizar los efectos en los usuarios del servicio (encapsulación por mensajes públicos en lugar de métodos públicos disponibles).

  • El área de la superficie del servicio se debe mantener pequeña. Cuanto mayor sea el número de interfaces públicas expuestas por un servicio, más difícil resultará su consumo y mantenimiento. Proporcionar pocas interfaces públicas bien definidas al servicio. Estas interfaces deben ser relativamente simples, y estar diseñadas para aceptar un mensaje de entrada bien definido y responder con un mensaje de salida igualmente bien definido. Una vez diseñadas las interfaces, éstas deben permanecer estáticas. Estas interfaces constituyen el requisito de diseño "constante" que los servicios deben admitir, que sirve como la cara pública para la implementación privada e interna del servicio.

  • Los detalles de la implementación interna (privada) no deben salir de los límites de un servicio. La entrada de los detalles de implementación en los límites del servicio dará lugar probablemente a un acoplamiento menos flexible entre el servicio y los usuarios del mismo. Los usuarios del servicio no deben conocer el mecanismo interno de la implementación del mismo, ya que esto limitaría las opciones de control o actualización de versiones del servicio. En la sección Antipatrones de este artículo se ofrece un ejemplo detallado de este problema

Principio 2: los servicios son autónomos

Los servicios son entidades que se implementan, versionan y administran de forma independiente. Los desarrolladores deben evitar hacer suposiciones en cuanto al espacio entre los límites del servicio, ya que es más probable que éste cambie a que lo hagan los propios límites. Por ejemplo, los límites de los servicios deberían ser estáticos con el fin de minimizar los efectos del control de versiones en el usuario. Mientras que los límites de los servicios son bastante estables, las opciones de implementación de servicios relativas a la directiva, ubicación física o topología de red son propensas al cambio.

Los servicios se pueden tratar dinámicamente a través de URI, lo que permite el cambio y la evolución en el tiempo de las ubicaciones subyacentes y las topologías de implementación, con escasa repercusión en el propio servicio (al igual que ocurre con los canales de comunicación de un servicio). Aunque estos cambios pueden tener efectos insignificantes en el servicio, sí que pueden repercutir considerablemente en las aplicaciones que lo consumen. ¿Qué ocurriría si uno de los servicios que se utilizan actualmente se moviera en el futuro a una red en Nueva Zelanda? El cambio en el tiempo de respuesta puede conllevar efectos no planeados o inesperados en los usuarios del servicio. Los diseñadores de servicios deben adoptar un punto de vista pesimista con respecto al consumo de los servicios: los servicios no funcionarán y sus comportamientos asociados (niveles de servicio) están sujetos a cambios. Los niveles adecuados de control de excepciones y lógica de compensación se deben asociar a las invocaciones de servicios. Asimismo, es probable que los usuarios de servicios deban modificar sus directivas con el fin de declarar tiempos de respuesta mínimos de los servicios que se van a consumir. Por ejemplo, puede que los usuarios de un servicio determinado requieran distintos niveles en cuanto a seguridad, rendimiento y transacciones, entre otros factores. Disponer de una directiva configurable permite que un único servicio admita varios SLA para la invocación de servicios (otras directivas se pueden centrar en el control de versiones y la localización, entre otros problemas). La comunicación de las expectativas de rendimiento a nivel de servicio conserva la autonomía, ya que no es necesario que los servicios conozcan las implementaciones internas de cada uno.

Los usuarios de servicios no son los únicos que deben adoptar un punto de vista pesimista en cuanto al rendimiento. Los proveedores de servicios deben mostrarse igualmente pesimistas a la hora de anticipar el modo en que se van a consumir los servicios. Se debe esperar que los usuarios utilicen los servicios de forma errónea, a veces sin ni siquiera notificar al propio servicio. Los proveedores de servicios tampoco pueden confiar en que los usuarios hagan "lo correcto". Por ejemplo, los usuarios pueden intentar comunicarse a través de mensajes mal formados/malintencionados o infringir otras directivas necesarias para la correcta interactuación de los servicios. El mecanismo interno del servicio debe intentar compensar dicho uso no adecuado, independientemente de la intención del usuario.

Aunque los servicios están diseñados para ser autónomos, ningún servicio es completamente independiente. Las soluciones basadas en SOA son fractales. Están formadas por una serie de servicios configurados para una solución específica. Desde el punto de vista de la autonomía de los servicios, se observa que no existe una autoridad que presida un entorno orientado a servicios. El concepto de "director" de orquesta es erróneo (lo que implica que el concepto de "restauración" entre servicios es también erróneo, aunque será mejor que dejemos este tema para otro artículo). Las claves para crear servicios autónomos son el aislamiento y el desacoplamiento. Los servicios se han diseñado e implementado de forma independiente entre sí y sólo se pueden comunicar a través de mensajes y políticas controlados por contratos.

Al igual que ocurre con otros principios de diseño de servicios, la experiencia con el diseño orientado a objetos también permite sacar conclusiones. El trabajo de Peter Herzum y Oliver Sims sobre las fábricas de componentes empresariales ofrece una visión general interesante sobre la naturaleza de los componentes autónomos. Aunque la mayor parte de su trabajo está destinado a la elaboración de soluciones basadas en componentes con un alto grado de cohesión, los principios de diseño básicos también son aplicables al diseño de servicios.

Teniendo en cuenta estas consideraciones, a continuación se enumeran algunos de los principios de diseño básicos que facilitan el cumplimiento del segundo principio de la integración orientada a servicios.

  • Los servicios se deben implementar y versionar de forma independiente al sistema en el que se implementan y consumen.

  • Los contratos se deben diseñar con la asunción de que una vez que se publiquen, no se podrán modificar. Este enfoque obliga a los desarrolladores a elaborar los diseños de sus esquemas de forma flexible.

  • Se debe garantizar la ausencia de errores en los servicios. Para ello, es necesario adoptar un punto de vista pesimista. Desde la perspectiva del usuario, se deben planear niveles no confiables de disponibilidad y rendimiento del servicio. Asimismo, desde el punto de vista del proveedor, se debe esperar que el usuario hará un mal uso del servicio (deliberado o no) y que cometerá errores, tal vez sin notificar al servicio

Principio 3: los servicios comparten esquema y contrato, pero no clase

Como se mencionó anteriormente, la interactuación del servicio se debe basar únicamente en políticas, esquemas y comportamientos basados en contratos. El contrato de un servicio se define generalmente a través de WSDL, mientras que los contratos de las agregaciones de servicios se pueden definir a través de BPEL (que, a su vez, utilizan WSDL para cada uno de los servicios agregados).

La mayoría de los desarrolladores definen clases para representar las distintas entidades en el espacio de un problema determinado (por ejemplo, Cliente, Orden y Producto). Las clases combinan comportamiento y datos (mensajes) en una construcción de un solo lenguaje de programación o con una plataforma específica. Los servicios alteran este modelo con el fin de maximizar la flexibilidad y la interoperabilidad. Los servicios que se comunican a través de mensajes basados en esquemas XML son independientes a los lenguajes de programación y a las plataformas, lo que garantiza mayores niveles de interoperabilidad. El esquema define la estructura y el contenido de los mensajes, mientras que el contrato del servicio define el comportamiento del propio servicio.

En resumen, el contrato de un servicio consta de los elementos siguientes:

  • Formatos de intercambio de mensajes definidos a través de esquemas XML.

  • Patrones de intercambio de mensajes (MEP) definidos a través de WSDL

  • Capacidades y requisitos definidos a través de WS-Policy.

  • BPEL se puede utilizar como contrato de nivel de proceso empresarial para la agregación de varios servicios

Los usuarios de los servicios utilizan el contrato del servicio para invocar e interactuar con el mismo. Por tanto, el contrato de un servicio debe permanecer estable con el paso del tiempo. Los contratos se deben diseñar de forma tan explícita como sea posible al tiempo que deben beneficiarse de la naturaleza extensible del esquema XML (xsd:any) y el modelo de procesamiento SOAP (encabezados opcionales).

El mayor reto que supone el tercer principio es la permanencia. Una vez que se ha publicado el contrato de un servicio, resulta muy difícil de modificar al tiempo que se minimizan los efectos en sus usuarios. La línea entre las representaciones de datos internos y externos es de vital importancia para la implementación y el uso adecuados de un servicio determinado. Los datos públicos (datos que se pasan entre servicios) se deben basar en estándares organizativos o verticales, con el fin de garantizar un alto nivel de aceptación entre servicios diferentes. Los datos privados, en cambio, (datos de un servicio) se encapsulan en el interior de un servicio. En cierto modo, los servicios se pueden considerar como representaciones más pequeñas de una organización que realiza transacciones de comercio electrónico. Del mismo modo que una organización debe asignar un pedido de compra externo a su formato de pedido, un servicio también debe asignar una representación de datos acordada contractualmente a su formato interno. Una vez más, se puede reutilizar la experiencia con la encapsulación de datos OO para ilustrar un concepto similar: la representación de datos internos de un servicio determinado sólo se puede manipular a través del contrato del servicio. Pat Helland analiza varios de los problemas relacionados con las representaciones de datos públicos y privados en "Datos externos frente a datos internos" (en inglés).

Teniendo en cuenta estas consideraciones, a continuación se enumeran algunos de los principios de diseño básicos que facilitan el cumplimiento del tercer principio de SO:

  • Garantizar que el servicio permanece estable con el fin de minimizar los efectos en los usuarios del mismo. En este sentido, el contrato hace referencia a la representación de datos públicos (datos), al patrón de intercambio de mensajes (WSDL) y a las capacidades configurables y los niveles de servicio (política).

  • Los contratos se deben diseñar para que sean lo más explícitos posible con el fin de minimizar la tergiversación. Asimismo, los contratos se deben diseñar para admitir futuras versiones del servicio a través de la extensibilidad tanto de la sintaxis XML como del modelo de procesamiento SOAP.

  • Mantener siempre definida la línea entre las representaciones de datos públicos y privados. El formato de los datos internos de un servicio determinado se debe ocultar a los usuarios al tiempo que el esquema de datos públicos debe permanecer inmutable (preferiblemente basado en un estándar organizativo, de facto, o industrial).

  • Versionar los servicios cuando los cambios en el contrato de los mismos sean inevitables. Este enfoque minimiza la ruptura de las implementaciones de los usuarios existentes

Principio 4: la compatibilidad de los servicios se basa en una directiva

Aunque a menudo se considera que este es el principio de diseño menos comprendido de todos, tal vez sea uno de los mas eficaces en cuanto a la implementación de servicios Web flexibles. No es posible comunicar determinados requisitos para la interactuación de servicios sólo en WSDL. Las expresiones de directiva se pueden utilizar para separar la compatibilidad estructural (lo que se comunica) de la semántica (cómo y a quién se comunica un mensaje).

Los requisitos operativos para los proveedores de servicios se pueden manifestar en forma de expresiones de directiva que pueda leer un equipo. Asimismo, las expresiones de directiva ofrecen un conjunto configurable de semánticas interoperables que controlan el comportamiento y las expectativas de un servicio determinado. La especificación de WS-Policy define un marco de directiva que puede leer un equipo capaz de expresar directivas de nivel de servicio, lo que les permite ser descubiertas y puestas en práctica en tiempo de ejecución. Por ejemplo, un servicio de seguridad gubernamental puede requerir una directiva que ponga en práctica un nivel de servicio específico (así, por ejemplo, las fotografías de pasaporte que cumplan con los criterios establecidos se deben comprobar a través de un sistema de identificación antiterrorista). La información de la directiva asociada a este servicio se podría utilizar con varios otros escenarios o servicios relacionados con la realización de una comprobación de segundo plano. WS-Policy se puede utilizar para poner en práctica estos requisitos sin que sea necesario escribir una sola línea de código adicional. Este escenario ilustra el modo en que el marco de una directiva proporciona información adicional sobre los requisitos de un servicio al tiempo que ofrece un modelo de programación declarativa para la definición y ejecución del servicio

Una aserción de directiva identifica un comportamiento, el cual es un requisito (o capacidad) del tema de una directiva. (En el escenario anterior, la aserción es la comprobación de segundo plano a través de un sistema de identificación antiterrorista.) Las aserciones proporcionan semánticas específicas de dominio y se definirán posteriormente en especificaciones independientes específicas de dominio para varias industrias verticales (lo que establecerá el concepto de "marco" de WS-Policy).

Aunque los servicios orientados a directivas continúan evolucionando, los desarrolladores deberían garantizar que su aserciones de directivas sean lo más explícitas posible en cuanto a las expectativas y las capacidades de semántica del servicio.

Patrones y antipatrones para SOA

Una vez comprendidos los conceptos de SOA (incluidos los principios de diseño orientado a servicios), pasemos a aplicar el contenido analizado. En el resto del artículo se analizan dos antipatrones y tres patrones. Cada uno de ellos se ha diseñado para servir de base a los conceptos tratados previamente.

Motivos para el uso de patrones y antipatrones

Tendemos a pensar y a comunicarnos en patrones. Christopher Alexander, autor de varias publicaciones sobre lenguajes de patrones, los define como "una abstracción de una forma concreta que es recurrente en contextos específicos y no arbitrarios". Los patrones y sus lenguajes permiten describir prácticas más adecuadas y diseños comprobados, así como capturar experiencias pasadas de modo que otros puedan aprender de ellas. Los patrones constituyen un enfoque eficaz que permite comprender con rapidez las directrices de diseño y los distintos contextos a los que se deben aplicar. Obviamente, los antipatrones denotan lo contrario. Mientras que los patrones ofrecen prácticas adecuadas y orientación eficaz, los antipatrones muestran errores de diseño comunes y constituyen un método adecuado para aprender de los errores cometidos por otros. En el resto del artículo se ofrece un resumen informal de dos antipatrones y tres patrones, los cuales se han diseñado a modo de guía para el desarrollo de servicios Web más eficaces.

Los patrones y antipatrones que se incluyen en este artículo siguen el formato que se indica a continuación:

  • Contexto: breve descripción del patrón o antipatrón. El contexto se proporciona para que el lector pueda reconocer posibles oportunidades para optar por uno u otro patrón, o para reconocer las características de un antipatrón antes de que se aplique.

  • Problema: instrucción simple del problema diseñada para formular los objetivos asociados al patrón o antipatrón.

  • Consideraciones adicionales: factores que se deben tener en cuenta a la hora de aplicar un patrón determinado o de reconocer un antipatrón

  • Solución: descripción de la solución para un antipatrón concreto o los pasos necesarios para aplicar el patrón asociado

  • Síntomas y consecuencias: en el caso de los antipatrones, los factores que motivan su aparición. En el caso de los patrones, se pueden abordar otros factores o consideraciones que se deben tener en cuenta antes de la aplicación del patrón asociado

Los antipatrones incluyen una recomendación adicional sobre la forma de mejorar sus errores de diseño.

Códigos de ejemplo

  • Para cada patrón existe código de ejemplo disponible para descarga.

  • Cada uno de los ejemplos se ha empaquetado como un archivo de instalación (MSI) e incluye un archivo Léame que explica su instalación y configuración

  • Como se indicó anteriormente, el lector debe ser cauto al utilizar el código de ejemplo. Este código es únicamente ilustrativo. Aunque la revisión, compilación y estudio del código de ejemplo resulta conveniente, se debe evitar su uso en un entorno de producción. MICROSOFT NO SE RESPONSABILIZA DE LOS DAÑOS QUE SE PUEDAN PRODUCIR EN CASO DE QUE EL LECTOR DECIDA UTILIZAR CUALQUIER FRAGMENTO DEL CÓDIGO DE EJEMPLO EN UN ENTORNO DE PRODUCCIÓN

Antipatrón nº 1: interfaz CRUDy

  • Contexto:

    • Diseño de servicios Web para el nuevo proyecto SOA de su empresa.

  • Problema:

    • ¿Cómo diseñar servicios SOA con .NET?

  • Consideraciones adicionales:

    • Es desarrollador de Visual Basic desde Visual Basic 5 y "sabe" cómo crear componentes.

    • Es su primer proyecto SOA.

    • La organización espera que otras aplicaciones de la plataforma utilicen su servicio

  • Solución:

    • La interfaz del servicio se diseña del mismo modo en que se hicieron las anteriores interfaces de componentes

    • Se crea un servicio que implementa las operaciones CRUD para crear, leer, actualizar y eliminar (Create, Read, Update y Delete).

    • En el ejemplo 1 se muestra un fragmento de código

Ejemplo 1. Servicio CRUD simple de Visual Basic .NET

<WebMethod()> _
Public Sub Create( ByVal CompanyName As String, ByVal 
ContactName As String, ByVal ContactTitle As String, 
ByVal Address As String, ByVal City As String, ByVal 
State As String, ByVal Zip As String, ByVal Country As 
String, ByVal Telephone As String, ByVal Fax As String) 

<WebMethod()> _
Public Function MoveNext() As Boolean
End Function

<WebMethod()> _
Public Function Current() As Object
End Function

<WebMethod()> _
Public Sub UpdateContactName( ByVal NewName as String)
End Sub

<WebMethod()> _
Public Function CommitChanges()
End Function

Síntomas y consecuencias:

  • El diseño de la interfaz motiva un comportamiento similar a RPC, al llamar a Create, MoveNext, etc., en lugar de enviar un mensaje bien definido que determine la acción que se llevará a cabo. Se trata de una infracción del primer (límites bien definidos) y tercer (compartir sólo esquema) principio

  • Es probable que la interfaz resulte algo compleja, ya que puede que los usuarios necesiten llamar a dos o tres métodos para realizar su trabajo

  • El uso de Sub para Create implica que el usuario no sabrá si la operación se realiza o no correctamente. Al diseñar un servicio siempre se deben tener en cuenta las expectativas del usuario: ¿qué información necesita?

  • Las operaciones CRUD constituyen el nivel equivocado de división de un servicio Web. Estas operaciones se pueden implementar en los servicios, pero no se deben exponer al usuario de este modo. Se trata de un ejemplo de servicio que permitía que capacidades internas (privadas) se filtraran en la interfaz pública del servicio

  • La interfaz implica interactuaciones con estado como, por ejemplo, la enumeración (consulte las funciones MoveNext y Current)

  • Los tipos abstractos (por ejemplo, el objeto que devuelve la función Current) dan lugar a un contrato débil. Este es otro ejemplo de infracción del tercer principio (compartir sólo esquema).

  • Se trata de un servicio muy peligroso, ya que los datos subyacentes se pueden dejar en un estado incoherente. ¿Qué sucedería si un usuario agregara un nuevo contacto (o actualizara un contacto existente) y nunca llamara a la función CommitChanges? Como se indicó anteriormente, los proveedores de servicios no pueden confiar en que el usuario "haga lo correcto".

Los riesgos y problemas mencionados se pueden evitar modificando el servicio de la siguiente manera:

  • Cambie la interfaz del servicio para comunicarse con el esquema XML. El esquema también puede incluir acciones del servicio de destino (por ejemplo, nuevo esquema de contacto o cambio de esquema de contacto). Los desarrolladores deben consultar los estándares de la industria existentes antes de desarrollar sus propios esquemas, ya que puede que ya exista un esquema que satisfaga sus necesidades

  • Encapsule la manipulación de datos tras métodos privados, a los que sólo se pueda obtener acceso a través de esquemas transmitidos a la interfaz pública

  • Compruebe que los usuarios del servicio reciban una respuesta con información que indique el estado de su solicitud.

Antipatrón nº 2: antipatrón "relajado"

  • Contexto:

    • Creación de una solución basada en servicios.

    • La organización está muy interesada en la reutilización del servicio.

  • Problema:

    • ¿Cómo se abordan las interfaces del servicio para maximizar los niveles de flexibilidad y extensibilidad?

  • Consideraciones adicionales:

    • Se está planeando ofrecer puntos de integración en todos los niveles de la solución.

    • Otros grupos necesitan tener acceso a la base de datos

  • Solución:

    • Las interfaces del servicio se diseñan como niveles de datos (consulte el ejemplo 2).

    • Se diseñan interfaces muy extensibles. Para ello, es preciso centrarse en el enlace en tiempo de ejecución

Ejemplo 2. Servicio de ejemplo de niveles de datos de Visual Basic .NET

<WebMethod()> _
Public Function QueryDatabase( ByVal Database as String, 
SQLQuery as string) As DataSet

<WebMethod()> _
Public Function Execute( ByVal Command as Integer, 
Arguments as string) As Boolean

Síntomas y consecuencias:

  • Prácticamente no hay contrato. El usuario del servicio no sabe cómo utilizarlo (por ejemplo, qué argumentos de comando son válidos, expectativas de codificación, etc.)

  • La interfaz se muestra demasiado liberal en cuanto a los elementos que acepta. El contrato es poco claro y existe un riesgo de seguridad elevado, susceptible de un ataque de inyección SQL

  • El contrato no ofrece suficiente información al usuario sobre cómo utilizar el servicio. Si el usuario debe leer información distinta a la firma del servicio para poder utilizarlo, será necesario revisar el uso del servicio

  • Antes de utilizar el servicio Web, el usuario se debe familiarizar con las estructuras de tablas y bases de datos, lo que dará lugar a un estrecho acoplamiento entre los usuarios y los proveedores del servicio.

  • El rendimiento se verá afectado debido a las dependencias del enlace en tiempo de ejecución y la codificación y descodificación entre los límites del mismo servicio

Los riesgos y problemas mencionados se pueden evitar modificando el servicio de la siguiente manera:

  • Cambie el contrato del servicio para comunicarse con un esquema XML bien definido. El esquema no debe exponer información sobre el almacenamiento de datos subyacente. La semántica del esquema debe proporcionar el contexto a los usuarios del servicio, permitiéndoles comprender su propósito. (Con este enfoque también mejorará el rendimiento del servicio.)

  • Las interactuaciones con la base de datos se deben encapsular en métodos privados, aislando al usuario de los detalles de la base de datos y sus tablas asociadas.

  • Compruebe que los usuarios del servicio reciben una respuesta con información que indique el estado de la solicitud.

Patrón nº 1: procesador de documentos

El código de ejemplo de este patrón se encuentra disponible para descarga.

  • Contexto:

    • Creación de una solución basada en servicios.

    • Se busca la compatibilidad con los principios de diseño orientado a servicios.

  • Problema:

    • ¿Cómo se crea un contrato bien definido y fácil de utilizar que sea compatible con los principios de diseño orientado a servicios?

  • Consideraciones adicionales:

    • El contrato para el servicio se debe orientar hacia un enfoque centrado en documentos

    • El contrato debe definir claramente la semántica del servicio (evitando el antipatrón "relajado")

    • El contrato debe promover un acoplamiento flexible. Para ello, debe encapsular la implementación en el propio servicio (evitando el antipatrón de la interfaz CRUDy). El contrato de servicio es un límite que no debe dejar escapar los detalles sobre implementaciones internas (recuerde el primer principio de diseño)

    • El servicio debe ser compatible con el perfil básico WS-I (en inglés), que permite su uso en cualquier plataforma o lenguaje de programación que admita servicios Web

    • El servicio debe representar un proceso empresarial como una unidad de trabajo completa (evitando suposiciones con estado similares a las del antipatrón de la interfaz CRUDy). Como se indicó anteriormente, los proveedores no pueden confiar en que el usuario "haga lo correcto". Un servicio no debe confiar en que el usuario llame a otro servicio para "restaurar" o "solucionar" un problema determinado si se genera una excepción.

  • Solución:

    • Defina o vuelva a utilizar un esquema XML para representar mensajes de solicitud y respuesta para el servicio. Compruebe que todas las interactuaciones públicas con el servicio emplean estos esquemas. (Consulte el ejemplo 3 para ver un fragmento de código.)

    • Genere objetos directamente desde los esquemas para agilizar el desarrollo. A este proceso se suele hacer referencia como un enfoque de "contrato como primer paso". El contrato se define antes del desarrollo del servicio actual. El contrato se puede utilizar posteriormente para generar el código del servicio. El enfoque de "contrato como primer paso" resulta eficaz a la hora de eliminar barreras en la interoperabilidad, ya que se basa en décadas de experiencia (CORBA, COM y DCE utilizaban lenguajes de interfaz). Los servicios Web suelen adoptar un enfoque de "contrato como último paso", ya que a menudo SOAP se utiliza sin WSDL para soluciones simples. No obstante, muchos entornos de desarrollo admiten el "contrato como primer paso", mientras que herramientas como WSCF y el generador de código de objetos XSD se encuentran disponibles para ayudar a automatizar este proceso

    • Como se mencionó con anterioridad, los proveedores de servicios no pueden esperar que los usuarios llamen o utilicen su servicio de forma específica. Esto significa que el uso de un proceso de compensación para una transacción determinada resulta aceptable, pero el proveedor no debe confiar en que el usuario del servicio desencadene la compensación. Los patrones de reserva (consulte la siguiente información) ofrecen la protección más autónoma para las transacciones, y elimina la dependencia del usuario del servicio.

Ejemplo 3. Servicio de ejemplo de procesador de documentos C#

[WebMethod()]

public FindCustomerByCountryResponse FindCustomersByCountry(
FindCustomerByCountry request) 
{
   ...
}

Síntomas y consecuencias:

Los servicios definidos que utilizan este sencillo patrón serán compatibles con los principios de diseño orientado a servicios

  • La asignación de un servicio Web centrado en documentos a un proceso empresarial resulta mucho más sencillo, ya que los clientes tienden a pensar en procesos empresariales como envío y recepción de documentos (se debe tener en cuenta que los documentos pueden representar eventos empresariales y no documentos empresariales reales).

  • El límite del servicio actúa como clara línea de transformación entre las representaciones de datos públicos y privados

  • Los detalles de implementación del servicio se encapsulan desde el usuario

  • La adopción de un enfoque de "contrato como primer paso" ayuda a asegurar una elevada interoperabilidad del servicio

  • Los servicios centrados en documentos pueden evolucionar con más facilidad, ya que todas las interactuaciones se producen a través del mensaje en lugar de mediante un método RPC no modificable

El uso de este enfoque conlleva algunos problemas de poca importancia que se deben mencionar

  • El rendimiento puede ocasionar conflictos con las transferencias de datos desde representaciones internas a externas.

  • Los usuarios deben asignar sus representaciones de datos al esquema que utilice el servicio. Algunos usuarios pueden considerar la asignación a un esquema del servicio como un proceso que supone pérdidas.

Patrón nº 2: mensaje inalterable

El código de ejemplo de este patrón se encuentra disponible para descarga

  • Contexto:

    • Se busca la compatibilidad con los principios de diseño orientado a servicios.

    • Creación de una solución transaccional basada en servicios.

    • El servicio debe ser capaz de corregir la situación si se producen varias entregas del mismo mensaje (por ejemplo, los mensajes deben ser inalterables).

  • Problema:

    • ¿Cómo se puede garantizar que los mensajes sean inalterables?

  • Consideraciones adicionales:

    • Del usuario sólo se puede esperar el contrato que defina su servicio

    • Se está trabajando con un sistema de base de datos transaccional que requiere actualizaciones frecuentes y confiables

    • Una plataforma de mensajería confiable puede no ser la respuesta rotunda, ya que los usuarios aún pueden enviar varias copias de la misma solicitud

  • Solución:

    • El contrato de servicio (esquema) debe indicar que los mensajes del usuario se etiqueten con un identificador de unidad de trabajo (al que se hará referencia como Id. de UDT). Consulte la figura 1.

    • El contrato no puede requerir que el Id. de UDT sea siempre exclusivo

    • El identificador representará una unidad de trabajo que sólo se realizará una vez, independientemente del número de mensajes que se reciban con el mismo valor de Id. de UDT

    • El servicio utilizará el identificador para determinar si una unidad ya se ha utilizado antes de iniciarla. Existen tres opciones posibles para administrar los identificadores asociados con trabajo ya realizado:

      Devolver una copia almacenada en caché de la respuesta.

      Procesar el mensaje de nuevo como si la primera solicitud nunca se hubiera recibido.

      Producir una excepción y devolver un error.

Figura 1. Patrón de mensaje inalterable

Síntomas y consecuencias:

La mensajería inalterable constituye un tema difícil. Las tres opciones para administrar identificadores duplicados presentan varias consideraciones:

  1. Devolver una respuesta almacenada en caché: esta opción requiere que el servicio mantenga una memoria caché de posibles respuestas durante un período de tiempo determinado. La determinación de los valores de tiempo de espera y actualización de la caché se debe establecer mediante el proceso empresarial asociado. Existen problemas adicionales que se deben considerar:

    ¿Qué implica que el valor actual sea diferente al valor almacenado en caché?

    ¿Qué sucede si la respuesta fue un error?

    ¿Qué ocurre si el usuario vuelve a utilizar identificadores de UDT para unidades no relacionadas?

  2. Volver a procesar el mensaje: aunque probablemente resulte una medida adecuada para operaciones sencillas de lectura, puede ser desastroso para escribir operaciones (por ejemplo, pago de una factura). ¿Qué sucede si se genera un error al procesar el Id. de UDT la primera vez que se recibe? Es probable que si se procesa de nuevo se obtenga el mismo error (este hecho se suele denominar "bucle de mensaje dañado").

  3. Generar una excepción: en este caso, puede que el usuario no haya recibido la respuesta original del servicio y simplemente se vuelva a enviar el mismo Id. de UDT (probablemente debido a un tiempo de espera en la respuesta). ¿Cómo recibirá el usuario la respuesta si se ha perdido la última enviada?

El Id. de UDT debe formar parte del esquema de la solicitud, relacionando la administración de las solicitudes duplicadas con el proceso empresarial asociado. Asimismo, el identificador también se puede agregar como encabezado SOAP personalizado, permitiendo que la administración de duplicados pase a formar parte de la infraestructura general de procesamiento de mensajes. También se debe incluir un identificador URI para el usuario para ayudar a detectar la reentrada. Se puede mantener automáticamente una pista de auditoría de las modificaciones, para permitir el seguimiento de las solicitudes de modificación de un Id. de UDT determinado. Finalmente, el problema asociado a la "actualización" de la memoria caché se puede abordar reflejando la respuesta en el momento de recepción de la solicitud. La administración de la caché constituye otro tema complejo que no se tratará en este artículo. (Para obtener más información sobre este tema, consulte el artículo de MSDN Concurrencia: diseño de interactuación entre servicios y sus agentes (en inglés).)

La compatibilidad con la mensajería inalterable aumenta la autonomía del servicio (primer principio de diseño), ya que los servicios no dependen de que los usuarios "hagan lo correcto". El uso de este enfoque conlleva algunos problemas de poca importancia que se deben mencionar:

  • Los servicios que emplean este patrón serán consumidores potenciales de grandes volúmenes de almacenamiento duradero para poder guardar las respuestas en la memoria caché.

  • Se pueden producir efectos importantes en el rendimiento del servicio debido a la administración de la caché.

Patrón nº 3: reserva

El código de ejemplo de este patrón se encuentra disponible para descarga.

  • Contexto:

    • Se busca la compatibilidad con los principios de diseño orientado a servicios.

    • Se cuenta con un proceso empresarial complejo que se desea exponer a los clientes con un servicio Web. Se trata de un proceso empresarial de ejecución larga con una sola transacción de varias horas.

  • Problema:

    • ¿Cómo se puede mantener la coherencia de los datos en un proceso de ejecución larga?

  • Consideraciones adicionales:

    • Las transacciones distribuidas no se pueden compartir.

    • El proceso empresarial descrito requiere que se completen varios mensajes

    • El tiempo necesario para el intercambio de mensajes varía de unos segundos a varias horas

  • Solución:

    • Los mensajes crean operaciones provisionales: estas operaciones son transacciones atómicas que dan coherencia a la base de datos.

    • Existen tres resultados posibles asociados a cada operación provisional:

      Confirmación de la operación provisional (con la realización del intercambio de mensajes).

      Cancelación de la operación, implícitamente debido a un error, o bien, explícitamente, llevada a cabo por uno de los participantes

      La operación (intercambio de mensajes) no se completa en los niveles de servicio esperados (tiempo de espera).

    • Un diálogo debe definir el seguimiento del estado de cada operación. La asignación de un identificador único (Id. de reserva) y la marca de hora de caducidad de cada diálogo permiten al servicio "recordar" donde se dejó cada diálogo. En la figura 2 se muestra una ilustración de este patrón.

    • Los usuarios del servicio que intenten confirmar una reserva registrada previamente deben incluir un Id. de reserva válido. El servicio rechazará las solicitudes de confirmación que no dispongan de identificador o que incluyan uno no válido.

    • La marca de hora de caducidad permite al servicio "finalizar" las reservas no confirmadas tras la conclusión de un período de tiempo determinado

Figura 2. Patrón de reserva

Síntomas y consecuencias:

  • La asignación de un Id. de reserva único y las marcas de hora de caducidad aseguran que los problemas de coherencia de datos se administren mediante el servicio y sus reglas empresariales asociadas. Como se indicó anteriormente, el servicio no puede contar con que el usuario "haga lo correcto".

  • Los Id. de reserva se utilizan para realizar un seguimiento de un diálogo concreto

  • Las marcas de hora de caducidad se emplean para tratar los tiempos de espera o los mensajes perdidos de forma previsible

El Id. de reserva y la marca de hora de caducidad deben formar parte del esquema de solicitud de confirmación, asociando el proceso de reserva al proceso empresarial real. El servicio genera los Id. de reserva y las marcas de hora de caducidad de cada solicitud, lo que permite que el servicio revise y "finalice" de forma periódica las reservas no confirmadas. Este patrón se puede combinar con el patrón de mensaje inalterable, protegiendo al servicio de solicitudes de reserva duplicadas.

El uso de este enfoque conlleva algunos problemas de poca importancia que se deben mencionar:

  • Las reglas empresariales para administrar las solicitudes de reserva se deben definir con claridad. ¿Cómo se administrará el "overbooking"?

  • El modelo atómico de confirmación en dos fases se intenta aplicar con frecuencia en situaciones donde no resulta adecuado (por ejemplo, procesos de ejecución larga). Estos procesos deben proteger la coherencia de los procesos atómicos desde los que se constituyen. El aislamiento de trabajo en un proceso de ejecución larga no es tarea fácil y el patrón de reserva es un sencillo intento de tratar esta cuestión.

Conclusión

Los cuatro principios de orientación a servicios ofrecen un conjunto diferenciado de reglas fundamentales que pueden servir de orientación para el proceso de desarrollo. En este artículo se han analizado varios patrones y antipatrones diseñados para ilustrar cómo influyen estos principios en los diseños del servicio. Asimismo, se han proporcionado directrices adicionales que ayudan a garantizar el éxito del futuro desarrollo y diseño del servicio:

  • Al dividir los servicios, base los procesos empresariales en documentos existentes y eventos empresariales reconocidos.

  • Aunque la flexibilidad es importante para las interfaces del servicio, no deje que el contrato del servicio subyacente sea poco claro

  • No espere que los usuarios "hagan lo correcto". Si el servicio requiere que el usuario ejecute una serie de pasos de forma predefinida, busque patrones (como el de reserva) que ayuden a realizar dichos pasos

  • No deje nunca el servicio ni sus recursos asociados en un estado incoherente

Existen diversos problemas de diseño adicionales asociados a los servicios Web. En posteriores artículos de esta serie se abordarán cuestiones como el control de versiones, la división de servicios y configuraciones de servicio orientadas a directivas.

Mostrar:
© 2015 Microsoft