6

We as a team are writing an C# SDK which communicates with a Server endpoint. All our API's till now have been Task based. Like

Task DoOperationAsync()

Recently we across a need for API which gets an intermediate update (file location) before the final one if the user opts for it.

DoSomething(bool interestedInIntermediateFile)

There are two option which came on the table

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

  2. Task < OpResult > DoSomething(Action < string > intermediateFile)

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

FileType - Intermediate, Final

CompletionReason - Reason (only applicable when FileType is final)

Wanted to know from the experts which would be a good API design.

Notes : We do see that some of the new future API would be observable based. So it will not be case that this is only observable API.


Adding a additional note - since it might not be so obvious from above.

CompletionReason contains a value only in the final update the intermediate update will have this as null. As the intermediate update, final result though related but not exact (in terms of populating fields) has been raised as a downside to observable. Is it a genuine downside.

Thanks in advance.

3
  • 1
    please don't cross-post: stackoverflow.com/questions/50054808/… "Cross-posting is frowned upon as it leads to fragmented answers splattered all over the network..."
    – gnat
    Apr 27, 2018 at 8:23
  • "CompletionReason being applicable only when FileType = Final has been raised as a downside to observable. Is it a genuine downside." Please clarify this. I don't see what the implication is here.
    – Sentinel
    Apr 27, 2018 at 14:56
  • Added more clarification to the completion reason point.
    – Shenron
    Apr 27, 2018 at 15:22

2 Answers 2

7

The decision on what technology to use is largely going to be about the skills in your team, and the audience to which your SDK is aimed, and to some extent whether or not your API will in the longer term benefit from being a provider of observable streams.

Rx.NET is a very powerful and flexible way of doing things, and I would recommend any .NET developer who needs to handle concurrency and/or streams of events to look into it.

(I assume that by "IObservable" you mean Rx.Net. Or are you looking into Akka Streams or some such?)

However, getting into the IObservable way of thinking can be a steep learning curve.

There is a lot of overlap between Task and Rx.NET. One explanation of the overlap is here but in my opinion is biased in favour of Task.

If you anticipate that your API will be dealing with asynchronous streams, such as returning long lists of data, or driving a push model, or wanting to combine multiple streams, then look into Rx.NET

If you anticipate that your API will benefit from LINQ over streams (with Rx.NET you can easily query IObservables as they are the 'push' version of IEnumerable, the 'pull' version), then look into Rx.NET. For example, look into .Where .Select .SelectMany .Buffer .Zip .Scan operators in Rx.NET and decide if you will have many uses for them.

If you see that your API will eventually become a push-model stream service and if you have the skills on hand, you might want to seriously consider Rx.NET

If it's just the small problem you described in your original post, it might not be worth the overhead of learning the Rx way unless you already have skills on hand. In essence, your chain of tasks (see Attached child tasks here ) is a dumbed down Rx IObservable. The advantage is that this code is intuitive for many developers. You don't need to view it as IObservable vs Callback, a Task is not a callback, and your child tasks (intermediate steps) can be returned as a collection of tasks (eg: you could compose your tasks as a list like final task + intermediate task, and then wait for When Any or some such, maybe attach the intermediate as child tasks).


Update: The concern you raise in your update that the operation completion message will be raised with a null value during intermediate steps makes no sense. Whichever approach you take, intermediate steps will not describe the operation completion reason, unless of course that intermediate step is post-hoc actualized as the final step. In the 1st scenario (Tasks): your intermediate action will not raise a completion reason (null). In the 2nd scenario (Observables): your intermediate actions will not raise a completion reason. What is the difference? What you should do in the case of the Observable is declare it an observable of "action results". This is semantically equivalent to Task (a future result). Declare X types of result message. Eg:

public class ResultMessage

public class FinalResultMessage: ResultMessage

etc

You can then implement Task or IObservable as you wish.

If you go the route of Observable. You can offer .Finally either in the subscriber or the observable to handle the FinalResultMessage.

In short, do not make the API an IObservable but IObservable if you go that route, and apply the same thinking for Task too.

14
  • This is the right answer. I would really prefer using Observable. But that is primarily because I have great experience working with it.
    – Euphoric
    Apr 27, 2018 at 8:01
  • @Euphoric, I disagree that this is the right answer. In my view, it's too focused on promoting a framework, rather than offering advice on software engineering principles. Not worthy of a downvote, but not quite good enough for +1 either.
    – David Arno
    Apr 27, 2018 at 8:38
  • @DavidArno Well, you could substitute the phrase Rx.NET with 'reactive programming' in the answer, but there are not many well maintained implementations of IObservable out there. Rx.NET in the .NET sphere is practically synonymous with the engineering principle of reactive programming. Essentially OP is asking for Task vs IObservable. Task itself is a framework.
    – Sentinel
    Apr 27, 2018 at 8:44
  • @Sentinel, Task is just an implementation detail. The question is asking whether to stick with the current notify on completion and add an optional notify on intermediate step mechanism; or switch over to a multiple notification step mechanism. Whether than latter mechanism is achieved via IObservable or Rx.NET is largely orthogonal to the question. In my view.
    – David Arno
    Apr 27, 2018 at 8:52
  • @DavidArno That's incorrect. Both approaches return multiple notifications. Task is a .NET framework sub-framework for returning futures. So is Rx.NET. There are no default implementations of IObservable in .NET, but Rx.NET is an MS supported/documented installable extension, so when someone starts talking about IObservable, they are usually talking about Rx. Perhaps OP should confirm.
    – Sentinel
    Apr 27, 2018 at 8:55
1

It depends on your API consumer.

Overall Though, I would try to make sure that you API matches to some extent your underlying server calls.

By Providing an Observable you have essentially introduced state into the equation. I need to hang onto that Observable until its finished sending me updates. The client needs to associate that Observable with incoming updates from the server related to the original request.

If your server is sending a constant stream of data then This would be a good match. (although you might want to look into a Stream)

If your server splits the job into two operations, a Start and a PollToGetResult for example. Then you have introduced state where you needn't have. It would be better to expose the separate operations on your API

4
  • There is difference in observables in WPF, which use INotifyPropertyChanged and observables using IObservable and RX stuff. WPF doesn't at all understand IObservable.
    – Euphoric
    Apr 27, 2018 at 10:59
  • ahh im getting mixed up with ObservableCollection
    – Ewan
    Apr 27, 2018 at 11:03
  • @Ewan I thought you might have been...the Rx stuff tends to delete state from your system, rather than introduce it. I agree it has to depend on the consumer anyway.
    – Sentinel
    Apr 27, 2018 at 12:49
  • I can see how it hides the state from your code
    – Ewan
    Apr 27, 2018 at 12:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.