-
Notifications
You must be signed in to change notification settings - Fork 1
Async Services
The base layer in MVVM is the model. The Model part consists of two types of classes: Data and Logic. Data classes should be pure "POCO" (Plain old CLR objects - a term that basically means that they should have only get-set properties with no logic). Logic classes are called Services. A Service is a decoupled unit of logic. It may have a state, and it should protect it so that the state may only be altered by directly invoking it's methods.
This is out philosophy when it comes to service development in MVVM:
- Each service should decide on which threads to run its methods. It is the services choice, not the callers. Use the inherited
Run
Method to make sure the method is executed on the correct thread or thread group. - Services may notify listeners on events, but they should allow the subscribers to decide on which threads the handler method runs. Use
AsyncEvent
to achieve that. - Each service method must return a
Task
object, to allow the caller to synchronize its logic. - Stateful services should protect their state. This basically means two things:
- Stateful services should coordinate execution of methods. Use the coordinated
Run
Method to make sure the method has exclusive access to state during execution. Other concurrent calls toRun
will be queued and postponed until the current execution completes. - Stateful services should allow to alter the state only by executing it's methods, Either by exposing immutable data objects, or by exposing only a copy of their state.
- Stateful services should coordinate execution of methods. Use the coordinated
Services may run in the background using the thread pool, they may run in the UI thread, they may even run on their own dedicated set of threads. One of the key concepts of MVVMKit is the the service should decide on which thread to run it's method. In many implementations of MVVM the services expose methods, and the calling party decides on which thread to run these methods. But MVVMKit believes that the service itself should be the one to decide the asynchronous context in which the service runs, thus maintaining what we call: Asynchronous Enncapsulation.
In MVVMKit, each service picks a
TaskScheduler
the the convention is that it runs all it's method using this TaskScheduler.
Note that not all services neccessarily run in the background. The NavigationService
for example, is a service that must run in the UI. But it may be invoked by a service that runs in the background, which may be invoked by a view model that runs in the UI.
Since the services get to choose the thread on which they run the method, they must return Task
objects in order to allow the caller to syncronize with the method completion.
In MVVMKit All service methods must return Task objects.
Just like the service gets to pick it's thread, the object calling the service has a thread it runs on. This causes a problem when we want to use the event listener pattern becuase the default implementation of C# events runs synchronously. We therefore provide the AsyncEvent
object which should used as an alternative. It makes sure to call each handler in the asynchronous context of it's choice (by default, the context used to subscribe). It calls all handlers concurrently and awaits their completion.