Diseño de API: observable vs devolución de llamada

7

Nosotros, como equipo, estamos escribiendo un SDK de C # que se comunica con un punto final del servidor. Todas nuestras API hasta ahora han sido basadas en tareas. Me gusta

Tarea DoOperationAsync ()

Recientemente, hemos necesitado una API que obtiene una actualización intermedia (ubicación del archivo) antes de la última, si el usuario la elige.

DoSomething (bool interestedInInmediateFile)

Hay dos opciones que aparecieron en la mesa

  1. Rx.IObservable < OpResult > DoSomething (bool interestedInIntermediateFile)

  2. Tarea < OpResult > DoSomething (Acción < cadena > intermedioFile)

Donde OpResult {Enum CompletionReason, enum FileType, string FileLoc}

Tipo de archivo - Intermedio, Final

CompletionReason - Reason (solo aplicable cuando FileType es final)

Quería saber de los expertos cuál sería un buen diseño de API.

Notas: Vemos que algunas de las nuevas API futuras serían basadas en observables. Por lo tanto, no será necesario que esto sea solo una API observable.

Agregar una nota adicional, ya que puede que no sea tan obvio desde arriba.

  

CompletionReason contiene un valor solo en la actualización final, la actualización intermedia tendrá esto como nulo. Como actualización intermedia, el resultado final, aunque relacionado pero no exacto (en términos de campos de población) se ha elevado como una desventaja a la observable. Es un verdadero inconveniente.

Gracias de antemano.

    
pregunta Shenron 27.04.2018 - 08:25

2 respuestas

7

La decisión sobre qué tecnología usar será en gran medida sobre las habilidades de su equipo y la audiencia a la que se dirige su SDK, y hasta cierto punto si su API se beneficiará a más largo plazo de ser un Proveedor de flujos observables.

Rx.NET es una forma muy poderosa y flexible de hacer las cosas, y recomendaría a cualquier desarrollador de .NET que necesite manejar la concurrencia y / o la transmisión de eventos para verlo.

(Supongo que por "IObservable" te refieres a Rx.Net. ¿O estás buscando en Akka Streams o algo así?)

Sin embargo, entrar en la forma de pensar de IObservable puede ser una curva de aprendizaje empinada.

Hay mucha superposición entre Task y Rx.NET. Una explicación de la superposición es aquí pero en mi opinión está sesgado a favor de la Tarea.

Si anticipa que su API tratará con flujos asíncronos, como devolver largas listas de datos, conducir un modelo push o querer combinar múltiples flujos, busque en Rx.NET

Si anticipa que su API se beneficiará de LINQ a través de las transmisiones (con Rx.NET, puede consultar fácilmente IObservables, ya que son la versión 'push' de IEnumerable, la versión 'pull'), luego busque en Rx.NET. Por ejemplo, busque en. En donde .Seleccione .SelectMany .Buffer .Zip .Scan operadores en Rx.NET y decida si tendrá muchos usos para ellos.

Si ve que su API eventualmente se convertirá en un servicio de transmisión de modelo push y si tiene las habilidades disponibles, es posible que desee considerar seriamente Rx.NET

Si es solo el pequeño problema que describiste en tu publicación original, puede que no valga la pena el costo de aprender Rx a menos que ya tengas habilidades disponibles. En esencia, su cadena de tareas (consulte Tareas secundarias adjuntas aquí ) es un tonto Rx IObservable. La ventaja es que este código es intuitivo para muchos desarrolladores. No necesita verlo como IObservable vs Callback, una Tarea no es una devolución de llamada, y sus tareas secundarias (pasos intermedios) pueden devolverse como una colección de tareas (por ejemplo: podría componer sus tareas como una lista como tarea final + tarea intermedia, y luego esperar a Cuando alguna o algo así, tal vez adjunte la tarea intermedia como tareas secundarias).

Actualización: la preocupación que presente en su actualización de que el mensaje de finalización de la operación se presentará con un valor nulo durante los pasos intermedios no tiene sentido. Cualquiera que sea el enfoque que tome, los pasos intermedios no describirán el motivo de finalización de la operación, a menos que, por supuesto, ese paso intermedio se actualice post-hoc como el paso final. En el primer escenario (Tareas): su acción intermedia no generará un motivo de finalización (nulo). En el segundo escenario (Observables): sus acciones intermedias no generarán un motivo de finalización. ¿Cuál es la diferencia? Lo que debe hacer en el caso del Observable es declararlo un observable de "resultados de la acción". Esto es semánticamente equivalente a la Tarea (un resultado futuro ). Declare X tipos de mensaje de resultados. Por ejemplo:

public class ResultMessage

public class FinalResultMessage: ResultMessage

etc

A continuación, puede implementar Task o IObservable como desee.

Si vas por la ruta del observable. Puede ofrecer .Finally en el suscriptor o en el observable para manejar el FinalResultMessage.

En resumen, no convierta la API en un IObservable sino en un IObservable si sigue esa ruta, y aplique el mismo pensamiento para la Tarea también.

    
respondido por el Sentinel 27.04.2018 - 09:53
1

Depende de su consumidor de API.

Sin embargo, en general, intentaría asegurarme de que su API coincida en cierta medida con las llamadas al servidor subyacente.

Al proporcionar un Observable, esencialmente has introducido un estado en la ecuación. Necesito aferrarme a ese Observable hasta que termine de enviarme las actualizaciones. El cliente debe asociar ese Observable con las actualizaciones entrantes del servidor relacionadas con la solicitud original.

Si su servidor está enviando un flujo constante de datos, esta sería una buena coincidencia. (aunque es posible que desee buscar en un Stream)

Si su servidor divide el trabajo en dos operaciones, un Inicio y un PollToGetResult, por ejemplo. Entonces has introducido el estado donde no es necesario. Sería mejor exponer las operaciones separadas en su API

    
respondido por el Ewan 27.04.2018 - 12:30

Lea otras preguntas en las etiquetas