¿Cuál es la mejor práctica para definir un servicio de jabón (operación genérica frente a específica)?

StackOverflow https://stackoverflow.com/questions/180572

  •  05-07-2019
  •  | 
  •  

Pregunta

Mi situación es la siguiente:

Tengo una base de datos normalizada, en la que tengo información geográfica sobre los aeropuertos. La estructura es:

airport --is in--> city --is in--> country --is in--> continent

Ahora quiero permitir que los usuarios administren estos datos, sin darles acceso directo a la base de datos. Necesitamos ofrecer esta interfaz de administración a través de un servicio web.

Ahora, cuando se trata de diseñar el servicio, nos topamos con la discusión sobre cómo definir las operaciones. Se nos ocurrieron diferentes soluciones:

Solución A: operaciones específicas

Para cada una de las cuatro tablas (aeropuerto, ciudad, país, continente) definimos 3 operaciones:

  • inserta
  • obtener
  • actualizar

Esto llevaría a 12 operaciones con 2 objetos de solicitud / respuesta = 24 objetos

Para crear un aeropuerto totalmente nuevo con todas las dependencias, serían necesarias al menos 4 solicitudes.

Solución B: genérico

Solo hay una operación, que se controla mediante parámetros. Esta operación es capaz de crear todo lo necesario para administrar la base de datos.

La operación decidiría qué se debe hacer y la ejecuta. Si ocurre un error, revertirá todo.

== > 1 Operación = 2 objetos de respuesta / solicitud altamente complejos

Solución C: Reunión en el centro 1

Una operación genérica por tabla, que es capaz de ejecutar get, insert, update, igual que la solución B, pero enfocada en una tabla cada una.

== > 4 operaciones = 8 objetos de petición / respuesta complejos

Solución D: Reunión en el centro 2

Una operación genérica por acción (obtener, insertar, eliminar), que puede funcionar en cada tabla y resolver dependencias.

== > 3 operaciones = 6 objetos de solicitud / respuesta un poco más complejos

Ejemplo

Como esto era bastante abstracto, muestre un ejemplo simplificado de los objetos de solicitud para crear (JFK / New York / USA / North America):

Solución A:

Solicitud 1/4:

<insertContinent>North America</insertContinent>

Solicitud 2/4:

<insertCountry continent="North America">USA</insertCountry>

Solicitud 3/4:

<insertCity country="USA">New York</insertCity>

Solicitud 4/4:

<insertAirport city="New York">JFK</insertAirport>

Solución B:

Solicitud 1/1:

<action type="insertCountry" parent="North America">USA</action>
<action type="insertAirport" parent="New York">JFK</action>
<action type="insertContinent" parent="">North America</action>
<action type="insertCity" parent="USA">New York</action>

Solución C:

Solicitud 1/4:

<countryAction type="insert" parent="North America">USA</countryAction>

Solicitud 2/4:

<airportAction type="insert" parent="New York">JFK</airportAction>

Solicitud 3/4:

<continentAction type="insert" parent="">North America</continentAction >

Solicitud 4/4:

<cityAction type="insert" parent="USA">New York</cityAction >

Solución D: Solicitud 1/1:

<insert airport="JFK" city="New York" country="USA" continent="North America" />

La solución D me parece bastante elegante, por lo que intenté poner esto en XSD:

Código:

<complexType name="NewContinent">
    <sequence>
        <element name="NAME" type="string"></element>
    </sequence>
</complexType>

<complexType name="NewCountry">
    <sequence>
        <element name="ISOCODE" type="string"></element>
        <element name="NAME" type="string"></element>
        <choice>
            <element name="newCONTINENT" type="tns:NewContinent"></element>
            <element name="CONTINENT" type="string"></element>
        </choice>
    </sequence>
</complexType>

<complexType name="NewCity">
    <sequence>
        <element name="IATA" type="string"></element>
        <element name="NAME" type="string"></element>
        <choice>
            <element name="COUNTRY" type="string"></element>
            <element name="newCOUNTRY" type="tns:NewCountry"></element>
        </choice>
    </sequence>

</complexType>

<complexType name="NewAirport">
    <sequence>
        <element name="IATA" type="string"></element>
        <element name="NAME" type="string"></element>
        <choice>
            <element name="CITY" type="string"></element>
            <element name="newCITY" type="tns:NewCity"></element>
        </choice>
    </sequence>

</complexType>

Una solicitud correspondiente se vería así:

<complexType name="Request">
    <choice>
        <element name="AIRPORT" type="tns:NewAirport"></element>
        <element name="CITY" type="tns:NewCity"></element>
        <element name="COUNTRY" type="tns:NewCountry"></element>
        <element name="CONTINENT" type="tns:NewContinent"></element>
    </choice>
</complexType>

Ahora mi pregunta: ¿Es esta realmente la mejor solución disponible? ¿El XSD es suficiente para entender, qué está pasando?

¿Fue útil?

Solución

Es de suponer que está escribiendo una capa de protocolo que comprenderá sus diferentes tipos de mensajes. También necesitará una capa de aplicación para analizar el contenido del mensaje. Los diferentes enfoques que menciona cambiarán la carga del análisis entre estas dos capas. Así por ejemplo:

Solución A : la capa de protocolo realiza todo el análisis y devuelve los datos y el comando. La capa de aplicación solo puede usar los datos. Esto también se conoce como el patrón RPC.

Pros: Puedes validar tus mensajes. Puede asignar mensajes directamente a las llamadas de la aplicación.

Contras: si necesita realizar un cambio en la interfaz, su protocolo cambia.

Solución B : la capa de protocolo devuelve dos valores y un comando. La capa de aplicación debe usar el comando para analizar los valores en tipos.

Pros: El protocolo nunca cambia.

Contras: No puedes validar mensajes. El código de tu aplicación es más complicado.

Solución C : la capa de protocolo devuelve dos tipos conocidos y un comando que se debe analizar. La capa de aplicación puede simplemente analizar el comando y usar los datos.

Ventajas: no puedo pensar en ninguna, parece que no es un compromiso muy bueno.

Contras: deja el análisis solo parcialmente hecho.

Solución D : la capa de protocolo devuelve tipos conocidos (la forma en que lo implementó) y un comando genérico. La capa de aplicación debe mirar los datos que recibe y convertir el comando genérico en un comando específico. Esto es similar a la arquitectura REST.

Pros: Las llamadas son operaciones distintas para que, por ejemplo, puedas obtener respuestas de la memoria caché.

Contras: Complejidad en la capa de aplicación

El modelo REST generalmente se implementa de manera diferente a lo que usted ha descrito. Utiliza mensajes HTTP GET, POST, PUT, DELETE para comunicar documentos arbitrarios. Los parámetros se dan como parte de la URL. Así por ejemplo:

<insert airport="JFK" city="New York" country="USA" continent="North America" />

se convierte en

<insert URL="airport?city=Chicago">ORD</insert>

O si está utilizando HTTP, se convierte en una solicitud POST a una URL del aeropuerto con un parámetro de la ciudad con el contenido como información sobre el aeropuerto. Tenga en cuenta que parte de esto se vuelve más claro con datos más conformes en los que tiene varios elementos y tipos mixtos. Por ejemplo, si desea enviar la abreviatura, el nombre largo y la altitud del aeropuerto.

Creo que la arquitectura REST podría funcionar bastante bien para la interfaz que describe. Mientras que todo lo que necesita hacer es apoyar las operaciones de CRUD. Hay muchos sitios que le brindarán las ventajas y desventajas del estilo arquitectónico REST.

Personalmente prefiero el estilo RPC (Solución A) con algunos atributos REST-ful. Quiero que el protocolo haga el trabajo de análisis y valide los mensajes. Por lo general, así es como la gente implementa las interfaces de servicios web SOAP.

Puede que su interfaz se vea simple hoy, pero mañana uno de sus clientes le pedirá una nueva llamada que no se ajuste tan bien al modelo REST y se encontrará encajándolo en los cuatro mensajes existentes.

Otros consejos

Esta es una pregunta antigua, y estoy seguro de que el servicio se ha escrito hace mucho tiempo, pero de todas formas quería contribuir con una respuesta.

El enfoque REST sería definir un recurso de aeropuerto, como este:

<airport href="/airports/JFK">
    <name>JFK</name>
    <city>New York</city>
    <country>USA</country>
    <continent>North America</continent>
</airport>

O, si desea utilizar un microformato compatible con el navegador:

<div class="object airport" href="/airports/JFK">
    <ul class="attributes"> 
        <li class="name">JFK</li>
        <li class="city">New York</li>
        <li class="country">USA</li>
        <li class="continent">North America</li>
    </ul>
</div>

Este recurso se ubicaría en un URI como / airports / JFK , que se recuperaría con el método GET , actualizado con un PUT método, y eliminado con un método DELETE .

En un diseño como este, el URI / airports / representaría un recurso contenedor para todos los aeropuertos en la base de datos, y los URI como / airports /? city = New + York y / airports /? country = USA serían filtros en el contenedor para devolver un subconjunto de los aeropuertos. Ambos serían métodos GET , y los recursos contendrían una lista de recursos del aeropuerto como se definió anteriormente, ya sea en su totalidad (ya que son pequeños) o con algunos atributos útiles y el href que apunta al recurso completo para cada aeropuerto.

Finalmente, agregar un nuevo recurso podría ser un método PUT en el URI completo del aeropuerto, o un método POST en / airports / . En ambos casos, el cuerpo de la solicitud es el recurso del aeropuerto como se muestra arriba. La diferencia entre los métodos es quién puede decidir el URI final para el aeropuerto: el cliente decide por PUT y el servicio decide por POST . El que use dependerá de si sus clientes pueden determinar razonablemente el URI correcto o no. Normalmente, el servicio decide porque los URI contienen un identificador único numérico y el servicio tiene que elegir eso.

Ahora, por supuesto, su pregunta original fue sobre SOAP, no REST. Seguiría adelante y establecería un diseño RESTful como lo describí, luego describiría mis recursos como tipos complejos utilizando XSD y un servicio SOAP con acciones que duplican el GET , PUT , DELETE y POST de las operaciones del servicio RESTful. Esto le dará el equivalente de RPC de:

class Airport
    has String name
    has String city
    has String country
    has String continent
    method void update(name, city, country, continent)
    method void delete()

class AirportList
    method Airport[] get(opt name, opt city, opt country, opt continent)
    method void add(name, city, country, continent)
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top