diff --git a/doc/Overview/Mvux/Overview.md b/doc/Overview/Mvux/Overview.md index b19f90468d..3457414795 100644 --- a/doc/Overview/Mvux/Overview.md +++ b/doc/Overview/Mvux/Overview.md @@ -4,82 +4,231 @@ uid: Overview.Mvux.Overview # MVUX Overview -MVUX is a variation of the MVU design pattern that will also feel familiar to developers who have previously worked with MVVM. +**M**odel, **V**iew, **U**pdate, e**X**tended (**MVUX**) is a variation of the MVU design pattern that encourages unidirectional flow of immutable data, whilst leveraging the data binding capabilities that makes MVVM pattern so powerful. -Its main advantages are cutting down on boilerplate code, easy maintenance of asynchronous data requests, and the ability to use immutable objects - -To better understand the capabilities of MVUX and the benefits it can provide, let us consider a simple application scenario. The application will display the current temperature, obtained from an external service. That should seem simple enough. All it needs to do is put a number on the screen. +To better understand MVUX, let us consider a weather application that will display the current temperature, obtained from an external weather service. At face value, this seems simple enough: call service to retrieve latest temperature and display the returned value. + Although this seems like an easy problem, as is often the case, there are more details to consider than may be immediately apparent. -- What if the external data isn't immediately available when starting the app? -- How to show that data is being initially loaded? Or updated? -- What if no data is available? +- What if the external service isn't immediately available when starting the app? +- How does the app show that data is being loaded? Or being updated? +- What if no data is returned from the external service? - What if an error occurs while obtaining or processing the data? -- How to keep the app responsive while requesting or updating the UI? +- How to keep the app responsive while loading or updating the UI? - How do we refresh the current data? - How do we avoid threading or concurrency issues when handling new data in the background? - How do we make sure the code is testable? -Individually, these questions and scenarios are simple to handle, but hopefully, they highlight that there is more to consider in even a very trivial application. Now imagine an application that you need to build, and with more complex data and UIs, the potential for complexity and the amount of required code can grow enormously. +Individually, these questions are simple enough to handle, but hopefully, they highlight that there is more to consider in even a very trivial application. Now imagine an application that has more complex data and user interface, the potential for complexity and the amount of required code can grow enormously. MVUX is a response to such situations and makes it easier to handle the above scenarios.  -We'll look at the pattern and how to use it as we walk through creating this app.  ## What is MVUX? -It stands for **M**odel, **V**iew, **U**pdate, e**X**tended. MVUX is a design pattern that extends MVU, making it an ideal design pattern in Uno Platform apps, utilizing code generation and the WinUI data-binding engine. +MVUX is an extension to the MVU design pattern, and leverages code generation in order to take advantage of the uniuqe data-binding engine of WinUI and the Uno Platform. -Looking at each of the MVUX elements, it's easiest to start with the View. +### Model -### View +The **Model** in MVUX is similar in many ways to the ViewModel in MVVM in that it defines the properties that will be available for data binding and methods that include any business logic. In MVUX this is referred to as the Model, highlighting that it is immutable by design. -The **View** is the UI. You can write it with XAML, C#, or a combination of the two, much as you would when using another design pattern. +For our weather app, `WeatherModel` is the **Model**, and defines a property named `CurrentWeather`. -You use data-binding to bind to the underlying presentation layer (we'll get to it soon) to keep the UI code (the View) separate from the presentation code. +```csharp +public partial record WeatherModel(IWeatherService WeatherService) +{ + public IFeed CurrentWeather => Feed.Async(this.WeatherService.GetCurrentWeather); +} +``` -### Model +The `CurrentWeather` property represents a feed (`IFeed`) of `WeatherInfo` entities (for those familiar with [Reactive](https://reactivex.io/) this is similar in many ways to an `Observable`). When the `CurrentWeather` property is accessed an `IFeed` is created via the `Feed.Async` factory method, which will asynchronously call the `GetcurrentWeather` service. -The **Model** in MVUX is fairly similar to the ViewModel in MVVM in that it embodies the presentation layer code, though, in MVUX, the ViewModel is referred to as just Model. +### View -In MVUX, change detection is similar to using the `INotifyPropertyChanged` interface, except MVUX automatically handles the change detection for you. This is achieved by a Proxy Model MVUX generates for each Model (that has the suffix 'Model'), and wraps around the source Model providing direct access to all its properties and methods, as well as extending it with additional features. +The **View** is the UI, which can be written in XAML, C#, or a combination of the two, much as you would when using another design pattern. For example the following can be used to data bind to the `CurrentWeather.Temperature` property. -MVUX thus enables working with immutable data structures ([C# records](https://learn.microsoft.com/dotnet/csharp/whats-new/tutorials/records)). Working with C# records can be a challenging transition for developers who have previously only worked with `INotifyPropertyChanged` implementing classes. -The fundamental difference is that since a record is immutable, making a change to a record means creating a new one with different data instead of modifying data within an existing one. Here's where the Proxy Model comes in handy and uses as a bridge between data-binding and change-notification, and immutable classes. +```xml + + + + + +``` + +If you're familiar with MVVM, the above XAML would look familiar, as it's the same XAML you would write if you had a ViewModel that exposed a CurrentWeather property that returns a WeatherInfo entity with a Temperature property. What's unique to MVUX is the additional information that `IFeed` exposes, such as when data is being loaded. For this, we can leverage the `FeedView` control that is part of MVUX. + +```xml + + + + + + + + + + + +``` + +The `FeedView` control is designed to work with an `IFeed`, and has different visual states that align with the different states that an `IFeed` can be in (e.g. loading, refreshing, error, etc.). The above XAML defines the ValueTemplate, which is required in order to display the Data from the `IFeed`. Other templates include ProgressTemplate, ErrorTemplate and NoneTemplate, which can be defined in order to control what's displayed depending on the state of the `IFeed`. ### Update -When the user provides input to the View, it triggers an **Update** that makes changes to the Model. +An **Update** is any action that will result in a change to the **Model**. Whilst an **Update** is often triggered via an interaction by the user with the **View**, such as clicking a Button, an **Update** can also be triggered from background processes (for example a data sync operation, or perhaps a notification triggered by a hardware sensor, such as a GPS). -In the standard MVU pattern, an update will cause a new View to be created upon each change. In MVUX the existing View will be updated via data-binding by the Bindable Proxy instead. +In the weather example, if we wanted to refresh the current weather data, a `Refresh` method can be added to the `WeatherModel`. -### eXtended +```csharp +public partial record WeatherModel(IWeatherService WeatherService) +{ + public IFeed CurrentWeather => Feed.Async(this.WeatherService.GetCurrentWeather); + public async ValueTask Refresh() { ... } +} +``` -The **eXtended** part of the pattern includes the toolset to adapt between the data-binding engine and the Model, which consists of generated code and and UI controls. +In the `View` this can be data bound to a `Command` property on a `Button`. -## Using MVUX to create an app +```xml + + + +