Skip to content
.NET MAUI for Architects: Designing for True Cross-Platform UI and Logic

.NET MAUI for Architects: Designing for True Cross-Platform UI and Logic

1 The Architectural Imperative for Cross-Platform Development

1.1 The Journey from “Mobile-First” to an Omni-Device Strategy

For the past decade, software design has been heavily shaped by a mobile-first mentality. The explosion of smartphones in the early 2010s pushed architects and development teams to prioritize mobile interfaces, sometimes even at the expense of desktop or web experiences. But the paradigm is evolving.

Today, user expectations are shaped by seamless workflows that span phones, tablets, desktops, and even embedded devices. Business users may begin a workflow on a tablet, refine it on a desktop, and finalize it on a mobile device, all in a single day. This shift demands an omni-device strategy—a way of thinking that prioritizes consistency, context, and continuity across form factors.

But how does one architect software that provides a truly unified experience? And how do you avoid being boxed into brittle patterns by device-specific technologies? The answer, increasingly, lies in modern cross-platform frameworks that enable shared logic, shared UI, and adaptable, native experiences.

1.2 Business Drivers: TCO, ROI, and Brand Cohesion Across Form-Factors

The technical rationale for cross-platform solutions is strong, but the business case is just as compelling.

Total Cost of Ownership (TCO): Maintaining separate codebases for iOS, Android, Windows, and macOS is expensive—not just in terms of development, but in QA, documentation, and ongoing maintenance. Code duplication multiplies bugs, testing matrices balloon, and release schedules become difficult to coordinate. A unified codebase reduces these costs significantly.

Return on Investment (ROI): Speed to market matters. Cross-platform frameworks let you deliver new features simultaneously on all major platforms. This synchrony can provide a crucial advantage, especially when targeting enterprise or consumer segments with high expectations for app quality and availability.

Brand Cohesion: Consistency in visual identity, behavior, and performance is a critical differentiator. Disjointed native apps can dilute brand value and frustrate users. A cross-platform approach makes it possible to deliver a cohesive experience while still embracing platform-appropriate idioms.

From an architectural perspective, the imperative is clear: organizations need robust, future-proof patterns for building, evolving, and scaling solutions that transcend individual devices.

1.3 Architectural Pitfalls: The “Lowest-Common-Denominator” & “Native-Divergence” Traps

Despite the clear benefits, cross-platform development introduces risks that must be anticipated and mitigated:

The Lowest-Common-Denominator Trap Many frameworks force developers to target only those features that are available across all platforms. This leads to bland, generic experiences that rarely feel “at home” anywhere. As an architect, you need to ensure your technology choice allows for rich, differentiated features while minimizing the cost of platform-specific extensions.

The Native-Divergence Trap On the opposite end, trying to accommodate every platform’s quirks and best practices can result in code that is tangled with conditional logic and platform shims. Over time, the supposed “single codebase” becomes a maintenance nightmare with poor testability and high onboarding costs.

The Solution: Design patterns such as dependency injection, interface segregation, and platform abstraction layers become crucial. Your cross-platform architecture should allow you to target the broadest shared feature set—while providing clean, maintainable points of extension for platform-specific capabilities.

1.4 The Rise of .NET MAUI: From Xamarin.Forms to a Unified Single-Project Model

Xamarin.Forms pioneered C# cross-platform UI but was limited by a renderer-based architecture and fragmented project setup. Each target platform had its own project, making asset management, build configuration, and customizations cumbersome.

.NET MAUI (Multi-platform App UI), introduced as part of .NET 6 and maturing rapidly through .NET 9 and early .NET 10, addresses these architectural challenges head-on:

  • Single-Project Structure: One project targets all platforms, streamlining build, asset, and resource management.
  • Handlers and Mappers: The new abstraction replaces renderers, allowing for more flexible, performant, and testable control customization.
  • Unified APIs: Access to device features and platform services is standardized through .NET MAUI Essentials.
  • Native Performance: Shared UI and logic are compiled to native binaries, utilizing platform controls and conventions.

With ongoing investment from Microsoft and a vibrant ecosystem, .NET MAUI is quickly becoming the most complete and future-ready solution for true cross-platform .NET development.

1.5 What’s New in .NET 9 / Early .NET 10: Workload Packaging, HybridWebView & TitleBar controls

.NET 9 (released November 2024) and early .NET 10 previews introduce several enhancements that are highly relevant for architects:

Workload Packaging: .NET workloads are now distributed as modular, updatable components. This decouples platform-specific SDKs (like Android and iOS) from the core SDK, making updates faster and dependency management more predictable.

HybridWebView: Building on the previous WebView control, HybridWebView enables sophisticated hybrid scenarios—embedding web-based UI seamlessly within native MAUI apps. Architecturally, this opens doors for progressive migration, experimentation, and leveraging existing web assets.

TitleBar Controls: Highly requested, .NET MAUI now allows for full customization of the application title bar across desktop platforms. This means your desktop experiences can be as branded and interactive as your mobile apps, without resorting to fragile platform hacks.

These improvements underscore .NET MAUI’s commitment to giving architects the tools needed for high-fidelity, cross-platform, brand-consistent apps.


2 .NET MAUI Platform Fundamentals

2.1 High-Level Stack: OS SDKs → .NET BCL → App Host

To design robust cross-platform apps, you need to understand the architectural layers in .NET MAUI:

  • Operating System SDKs: Native APIs for Windows (WinUI), macOS (Catalyst), iOS, and Android provide access to device hardware, UI controls, and OS services.
  • .NET Base Class Library (BCL): The unified set of .NET APIs (System., Microsoft.) exposes a consistent programming model across all platforms.
  • App Host / MAUI Handlers: The .NET MAUI runtime abstracts platform differences. Handlers translate MAUI controls into native views at runtime.
  • Your App: C#/XAML code, including your business logic, UI definitions, and data models.

This layered approach means architects can design around platform-agnostic patterns—while still maintaining points of extensibility for advanced scenarios.

2.2 The Single Project Layout Explained

One of the most significant innovations in .NET MAUI is the single-project structure. Instead of separate projects for iOS, Android, Windows, and macOS, you work within a single .NET MAUI project.

Key Features:

  • Platform folders: /Platforms/Android, /Platforms/iOS, /Platforms/Windows, and /Platforms/MacCatalyst hold platform-specific code, assets, and resources.
  • Shared Resources: Images, fonts, and raw assets are managed centrally in the Resources folder. Build targets automatically optimize and bundle these assets for each platform.
  • Conditional Compilation: Use preprocessor directives (#if ANDROID, #if IOS, etc.) to insert platform-specific code only where necessary.

Sample Structure:

MyApp/

├── Platforms/
│   ├── Android/
│   ├── iOS/
│   ├── MacCatalyst/
│   └── Windows/

├── Resources/
│   ├── Images/
│   ├── Fonts/
│   └── Raw/

├── Views/
├── ViewModels/
├── Models/
└── App.xaml.cs

This design makes onboarding new developers easier and reduces context-switching between platforms.

2.3 Workload Management & Version Pinning (Handling Xcode / Android SDK Drift)

Cross-platform mobile development is often hampered by SDK drift—the constant evolution of Xcode, Android SDKs, and their dependencies. .NET MAUI’s workload system addresses this:

  • Workload Installation: Platform support (e.g., Android, iOS) is delivered as modular workloads. Use dotnet workload install maui-android to add support for a specific target.
  • Version Pinning: Projects can specify required workload versions, ensuring all team members and CI/CD agents build with compatible toolchains. This minimizes “works on my machine” syndrome and broken builds after SDK updates.

Example: Add a global.json to pin .NET SDK and workload versions:

{
  "sdk": {
    "version": "9.0.100"
  },
  "msbuild-sdks": {
    "Microsoft.DotNet.Maui.Sdk": "9.0.0-preview.7"
  }
}

Architects should enforce workload versioning in CI/CD and document required workloads in developer onboarding guides.

2.4 Build Pipeline: C#/XAML → IL → AOT/JIT → Native Binaries

The .NET MAUI build pipeline is optimized for both performance and debuggability.

Pipeline Overview:

  1. Source Code: Your C# and XAML files.

  2. Compilation: The C# compiler emits Intermediate Language (IL).

  3. Linking: Unused code is stripped to minimize app size.

  4. AOT/JIT Compilation:

    • iOS/macOS: Ahead-of-Time (AOT) compilation is mandatory. IL is compiled into native ARM64/x64 code.
    • Android: Default is mixed mode (AOT + JIT), optimizing for cold start and runtime performance.
    • Windows: JIT compilation using CoreCLR.
  5. Packaging: Output is bundled into platform-specific packages (APK, IPA, MSIX).

Understanding this flow is key for performance tuning and for integrating with CI/CD pipelines.

2.5 Handlers & Mappers: Replacing Renderers, Extending Controls

In Xamarin.Forms, custom controls were implemented using “renderers.” These required platform-specific subclasses for each control—introducing complexity and performance overhead.

.NET MAUI replaces renderers with a Handler architecture. Handlers are lightweight, composable, and fast. Here’s how it works:

  • Handler: An object that translates a MAUI control (e.g., Button) into its platform-native view (UIButton on iOS, AppCompatButton on Android).
  • Mapper: A dictionary that maps MAUI property changes (e.g., TextColor, FontSize) to native property changes.

Customizing a Control with Handlers

Suppose you want to customize the appearance of all Buttons on Android. In your MauiProgram.cs, you can add:

using Microsoft.Maui.Handlers;

public static MauiApp CreateMauiApp()
{
    var builder = MauiApp.CreateBuilder();

    builder.ConfigureMauiHandlers(handlers =>
    {
        handlers.AddHandler<Button, ButtonHandler>((view) =>
        {
            view.PropertyMapper.Append(nameof(Button.BackgroundColor), (handler, button) =>
            {
                handler.NativeView.SetBackgroundColor(Android.Graphics.Color.Orange);
            });
        });
    });

    // ... other builder setup
    return builder.Build();
}

Benefits for Architects:

  • Encapsulation: Handlers encapsulate platform differences, reducing the need for fragile custom renderers.
  • Testability: Handlers can be unit tested in isolation.
  • Extensibility: Easily extend or override control behavior without global side effects.

2.6 Essentials API Layer vs. Full Platform APIs

.NET MAUI Essentials provides a unified API for common device features—location, battery, sensors, secure storage, connectivity, and more. This means most business and UX logic can be written once, without diving into platform-specific APIs.

Example: Accessing battery information via Essentials:

using Microsoft.Maui.Essentials;

double batteryLevel = Battery.ChargeLevel;
BatteryState state = Battery.State;

But sometimes, you need access to features not covered by Essentials. In these cases, you can:

  • Use platform-specific code in /Platforms/* folders
  • Write dependency services or use interfaces with platform-specific implementations

Best Practice: Architect for the 80% case with Essentials APIs, and abstract away platform specifics behind clean interfaces for the remaining 20%. This minimizes code duplication and eases maintenance.

2.7 Tooling Deep-Dive: Hot Reload, Live Preview, Dev Tunnels

Modern development demands rapid feedback. .NET MAUI offers several advanced tooling features:

  • Hot Reload: Change XAML or C# code and see updates instantly in running apps—no rebuild required. This accelerates UI iteration and reduces context switching.
  • Live Preview: See real-time previews of your UI as you edit XAML, directly within Visual Studio. This helps architects and designers collaborate more efficiently.
  • Dev Tunnels: Securely expose your development environment to the internet, enabling remote device testing and debugging without cumbersome network setup.

Example Workflow: You adjust a layout in XAML, hit save, and immediately see the updated screen on your Android device, iOS simulator, or Windows desktop—all without a full redeploy.

These features support more agile, collaborative, and productive development processes, which is essential when leading diverse teams on complex, cross-platform projects.


3 Designing the Application Core – Maximizing Shared Logic

At the heart of any modern cross-platform application lies a robust, maintainable application core. The architectural decisions you make here shape not just code reuse and testability, but also team velocity and long-term adaptability. .NET MAUI, while flexible, rewards a thoughtful approach to core design—one that minimizes platform-specific leakage and embraces modular, future-proof patterns.

3.1 Selecting an Architectural Pattern

3.1.1 Why MVVM Still Rules

For the vast majority of business-driven, UI-heavy apps, the Model-View-ViewModel (MVVM) pattern remains the best fit. MVVM offers several benefits for .NET MAUI architects:

  • Separation of Concerns: UI logic stays decoupled from business and data logic.
  • Testability: ViewModels can be unit tested in isolation from the UI.
  • Reusability: Logic can be reused across multiple views and even platforms.

Key .NET features enable MVVM to shine:

  • INotifyPropertyChanged: This interface, foundational in data binding, enables the View to react to property changes on the ViewModel. In MAUI, data binding is at the core, making INotifyPropertyChanged implementation non-negotiable for any reactive UI.

    public class UserProfileViewModel : INotifyPropertyChanged
    {
        private string _username;
        public string Username
        {
            get => _username;
            set
            {
                if (_username != value)
                {
                    _username = value;
                    OnPropertyChanged();
                }
            }
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
            => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
  • ICommand: Commands encapsulate user actions—like button presses—enabling ViewModels to trigger logic in a loosely coupled way. This is central to testability and separation.

3.1.2 Framework Showdown: CommunityToolkit MVVM vs Prism vs ReactiveUI

While you can hand-roll MVVM infrastructure, most teams benefit from using a well-supported library. Let’s examine the top contenders:

CommunityToolkit.MVVM: Now the recommended default for .NET MAUI. Offers source generators, powerful attributes (like [ObservableProperty]), strong typing, and zero external dependencies. Maintained by Microsoft, its lightweight nature means it fits naturally in MAUI’s startup pipeline.

  • When to use: Simpler projects, or when you want minimal “framework magic” and maximum .NET idiomatic code.

Prism: A mature, enterprise-friendly MVVM framework. Provides advanced features: region management, powerful dependency injection abstractions, navigation services, and event aggregation.

  • When to use: Complex, modular apps (e.g., LOB or enterprise-grade), where navigation and loose coupling are priorities.

ReactiveUI: MVVM with a reactive programming backbone. Emphasizes streams, observables, and functional paradigms.

  • When to use: Highly dynamic UIs, apps that need fine-grained state flow control, or when architecting for “push”-oriented data.

Comparison Table:

FrameworkStrengthsIdeal For
CommunityToolkit.MVVMLightweight, Source GeneratorsMAUI-first, simplicity
PrismModular, Navigation, DILarge, modular, scalable apps
ReactiveUIReactive streams, ComposabilityDynamic, stateful, real-time apps
Example: MVVM With CommunityToolkit.MVVM
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;

public partial class CounterViewModel : ObservableObject
{
    [ObservableProperty]
    private int count;

    [RelayCommand]
    private void Increment()
    {
        Count++;
    }
}

Here, attributes generate the INotifyPropertyChanged boilerplate and ICommand implementations, streamlining your ViewModel.

3.2 Dependency Injection as a First-Class Citizen

3.2.1 Bootstrapping in MauiProgram

In .NET MAUI, dependency injection (DI) is built-in via the .NET Generic Host model. All services—whether for data, business logic, or platform access—should be registered in MauiProgram.cs. This centralizes configuration and enables true inversion of control.

Example:

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder.Services.AddSingleton<ISettingsService, SettingsService>();
        builder.Services.AddScoped<IDataRepository, SqliteDataRepository>();
        builder.Services.AddTransient<LoginViewModel>();
        // ... more service registrations
        return builder.Build();
    }
}

3.2.2 Lifetimes: Singleton, Scoped, Transient

  • Singleton: One instance per app lifetime. Use for stateless or shared services (e.g., logging, settings).

  • Scoped: One instance per scope. Not as relevant in MAUI as in ASP.NET, but can be used to manage short-lived contexts (e.g., per navigation flow).

  • Transient: New instance every injection. Best for ViewModels and lightweight services that maintain no state between requests.

Choose lifetimes carefully to balance memory usage, startup time, and testability.

3.3 Data Access Layer

3.3.1 Generic Repository + Unit-of-Work

Abstracting data access is essential for portability and testability. The Repository pattern provides a way to work with domain entities as in-memory collections, hiding the underlying data store details.

Example: Generic Repository Interface

public interface IRepository<T> where T : class
{
    Task<T> GetByIdAsync(Guid id);
    Task<IEnumerable<T>> GetAllAsync();
    Task AddAsync(T entity);
    Task UpdateAsync(T entity);
    Task DeleteAsync(T entity);
}

The Unit-of-Work pattern coordinates the work of multiple repositories by tracking changes and ensuring atomic commits.

Example:

public interface IUnitOfWork : IDisposable
{
    IRepository<User> Users { get; }
    IRepository<Order> Orders { get; }
    Task<int> CommitAsync();
}

3.3.2 Online/Offline Sync & Conflict Resolution

Mobile and edge devices demand resilient data strategies. Architecting for online/offline use cases is no longer optional. This involves:

  • Local Caching: Store data in SQLite or other local stores using EF Core or LiteDB.
  • Sync Engine: Schedule and manage synchronization with cloud APIs, handling network drops and retries.
  • Conflict Resolution: Implement strategies (e.g., client-wins, server-wins, merge policies) for handling data discrepancies.

Practical Example: Suppose you’re building a field service app. Users may edit work orders offline. The sync layer should queue changes locally and resolve conflicts intelligently when the device reconnects.

3.4 Business-Logic Services

3.4.1 Cross-Platform AuthenticationService, SettingsService

Business logic services are the backbone of your shared codebase. Two common examples:

AuthenticationService:

  • Supports OAuth2, OpenID Connect, and native platform auth providers.
  • Uses Essentials for secure storage.
  • Encapsulates platform-specific APIs behind an interface, e.g., using conditional compilation or dependency services.

SettingsService:

  • Abstracts user settings storage.
  • Uses Preferences (MAUI Essentials) or platform key-value stores.

Example:

public class SettingsService : ISettingsService
{
    public string GetSetting(string key) => Preferences.Get(key, string.Empty);
    public void SetSetting(string key, string value) => Preferences.Set(key, value);
}

Architects should centralize these services for consistency and maintainability.

3.5 Clean Architecture / Modular Monolith

3.5.1 Layers: Domain → Application → Infrastructure → Presentation

Clean Architecture—popularized by Robert C. Martin—places the business domain at the core, surrounded by concentric rings of application, infrastructure, and presentation.

  • Domain Layer: Business entities, aggregates, value objects, and domain services. No dependencies on infrastructure or UI.
  • Application Layer: Orchestrates use cases, DTOs, and application services. Depends on domain abstractions.
  • Infrastructure Layer: Implements data access, external API calls, file I/O. Depends on abstractions from inner rings.
  • Presentation Layer: MAUI UI logic—Views, ViewModels, navigation.

Why does this matter? It maximizes portability and testability. Swapping out the UI (e.g., MAUI to Blazor) or data store becomes feasible without rewriting business logic.

Project Structure Example:

/MyApp.Domain
/MyApp.Application
/MyApp.Infrastructure
/MyApp.Presentation (MAUI)

3.5.2 Vertical-Slice CQRS Option

Some architectures benefit from vertical-slice approaches—CQRS (Command Query Responsibility Segregation)—where features are organized by business capability rather than technical layer. Each slice owns its requests, handlers, and data access, promoting high cohesion and lower coupling.

CQRS Example:

  • /Features/Order/CreateOrder
  • /Features/Order/GetOrderDetails
  • Each feature includes its command, handler, and possibly UI.

For complex, modular systems, this enables granular scaling and clearer ownership.

3.6 Async & Reactive Programming (Task, ValueTask, IAsyncEnumerable, Rx)

Modern MAUI apps are expected to be fast and responsive. Synchronous code can block the UI and degrade experience.

  • Task and ValueTask: Use async/await extensively for I/O, networking, and CPU-bound operations.

    public async Task<IEnumerable<User>> GetUsersAsync() =>
        await _repository.GetAllAsync();
  • IAsyncEnumerable: Allows asynchronous streaming of data—ideal for paged or live data scenarios.

    public async IAsyncEnumerable<Message> GetMessagesStreamAsync()
    {
        // yields messages as they arrive
        yield return await FetchNextAsync();
    }
  • Reactive Extensions (Rx): For advanced event-driven and push-based scenarios, Rx enables composable streams and declarative event logic.

    IObservable<string> textChanges = textBox
        .GetPropertyChangedObservable()
        .Select(e => e.NewValue as string)
        .Throttle(TimeSpan.FromMilliseconds(300));

For architects, understanding when to use each approach is key. Use Task for most operations, ValueTask for high-throughput, short-lived results, and Rx for complex, time-based state management.

3.7 Resilience with Polly (Retry, Circuit-Breaker, Timeout)

Resilience is not just a backend concern. Mobile and cross-platform apps must gracefully handle unreliable networks, slow APIs, and transient errors.

Polly—the .NET resilience library—integrates cleanly with MAUI services.

  • Retry: Automatically retries failed operations.

    var retryPolicy = Policy
        .Handle<HttpRequestException>()
        .RetryAsync(3);
    
    await retryPolicy.ExecuteAsync(() => FetchRemoteDataAsync());
  • Circuit-Breaker: Prevents repeated failures from overwhelming your app.

    var circuitBreaker = Policy
        .Handle<TimeoutException>()
        .CircuitBreakerAsync(2, TimeSpan.FromMinutes(1));
  • Timeout: Imposes maximum durations on tasks.

Integrate Polly policies into HTTP clients, sync engines, and critical service calls to provide a robust user experience, even under suboptimal conditions.

3.8 Logging, Telemetry & Config (Serilog, App Insights, Feature Flags)

Visibility into app behavior is crucial for diagnosing production issues and understanding user journeys.

  • Serilog: Structured, extensible logging with rich sink support (files, Seq, etc.).

    Log.Logger = new LoggerConfiguration()
        .WriteTo.File("logs/myapp.txt")
        .CreateLogger();
  • Application Insights: Real-time telemetry and user analytics. App Insights SDK can be integrated for cross-platform event tracking and exception logging.

  • Feature Flags: Services like LaunchDarkly or Azure App Configuration enable toggling features at runtime, aiding in staged rollouts and A/B testing.

Config management (appsettings.json, secrets.json) is increasingly supported, but architects should design config access to respect platform constraints and security best practices.


4 Architecting the User Interface

The user interface is your users’ first and most persistent impression of your solution. For software architects, the challenge is to design a UI layer that is expressive, maintainable, and truly cross-platform—leveraging MAUI’s capabilities while delivering a best-in-class experience everywhere.

4.1 Layouts & Controls

4.1.1 Grid vs FlexLayout vs StackLayout

  • Grid: Most versatile for complex UIs. Offers precise, two-dimensional placement (rows, columns, spans). Ideal for dashboards, forms, and structured content.

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <Label Text="Username" Grid.Row="0" />
        <Entry Grid.Row="1" />
    </Grid>
  • StackLayout: Vertical or horizontal stacking. Good for simple, linear layouts.

    <StackLayout Orientation="Vertical">
        <Label Text="Welcome"/>
        <Button Text="Continue"/>
    </StackLayout>
  • FlexLayout: Inspired by CSS Flexbox. Great for responsive layouts that need wrapping, alignment, and distribution.

    <FlexLayout Direction="Row" Wrap="Wrap">
        <BoxView WidthRequest="50" HeightRequest="50" />
        <BoxView WidthRequest="50" HeightRequest="50" />
        <!-- More items -->
    </FlexLayout>

Architects should select layouts based on required complexity, responsiveness, and future flexibility.

4.1.2 Data-Driven UIs: CollectionView, CarouselView

  • CollectionView: Replaces ListView as the go-to for lists, grids, and virtualized data. Highly customizable and performant.

  • CarouselView: For swipeable, page-based UIs (think onboarding, image galleries).

    <CollectionView ItemsSource="{Binding Items}">
        <CollectionView.ItemTemplate>
            <DataTemplate>
                <Label Text="{Binding Name}" />
            </DataTemplate>
        </CollectionView.ItemTemplate>
    </CollectionView>

Binding these controls to ObservableCollections in your ViewModels enables live, reactive UIs.

4.2 Styling & Theming

4.2.1 Centralised ResourceDictionary + Theme Switcher

Consistency in styling is non-negotiable for cross-platform brand identity.

  • ResourceDictionary: Store colors, fonts, and styles centrally. Reference them in XAML or code-behind.

    <ResourceDictionary>
        <Color x:Key="PrimaryColor">#2196F3</Color>
        <Style TargetType="Button">
            <Setter Property="BackgroundColor" Value="{StaticResource PrimaryColor}"/>
        </Style>
    </ResourceDictionary>
  • Theme Switcher: Enable runtime switching between light and dark themes, or even custom themes.

    Example: Dynamically swap ResourceDictionaries at runtime to enable dark mode.

Architects should enforce centralized styling to minimize drift and ease rebranding.

4.3 Adaptive UX

4.3.1 Breakpoints (DisplayInfo) & Desktop-First Considerations

True cross-platform UI must adapt gracefully to screen sizes and orientations.

  • DisplayInfo: MAUI’s DeviceDisplay.MainDisplayInfo gives you screen width, height, and orientation. Use this for responsive breakpoints.

    var width = DeviceDisplay.MainDisplayInfo.Width;
    if (width > 800)
        // Show two-pane layout
  • Desktop-First: Many cross-platform apps now target desktop and tablets as primary. Consider mouse/keyboard navigation, window resizing, and multiple monitor support.

4.3.2 Platform-Specific Tweaks: OnPlatform, #if directives

  • OnPlatform: XAML syntax for customizing values by platform.

    <Label Text="Welcome">
        <Label.FontSize>
            <OnPlatform x:TypeArguments="x:Double">
                <On Platform="iOS" Value="20"/>
                <On Platform="Android" Value="18"/>
                <On Platform="WinUI" Value="22"/>
            </OnPlatform>
        </Label.FontSize>
    </Label>
  • #if Directives: Use for complex platform-specific logic in C#.

    #if ANDROID
        DoAndroidStuff();
    #elif IOS
        DoIosStuff();
    #endif

The goal is to deliver a “native” feel everywhere without forking the codebase.

4.4 Accessibility (Semantics, AutomationProperties, Color-Contrast)

Accessibility must be built in from day one. .NET MAUI provides:

  • Semantics: Add descriptions, hints, and roles for screen readers.

    <Button Text="Submit" SemanticProperties.Description="Submits the form"/>
  • AutomationProperties: Set accessibility IDs and help text for testing and assistive technologies.

  • Color Contrast: Design ResourceDictionaries to pass WCAG color contrast guidelines.

Architects should incorporate accessibility checks into design reviews and CI/CD pipelines.

4.5 Localization & Globalization (Resx, RTL, ICU)

Localization and globalization are critical for global apps.

  • Resx: Resource files for storing strings per locale. Use in both XAML and C#.

    label.Text = Resources.WelcomeMessage;
  • RTL Support: MAUI supports right-to-left layouts. Design your layouts to flip seamlessly.

  • ICU: International Components for Unicode provide advanced text shaping and formatting.

Plan for localization early. Architect resource access through interfaces for maximum flexibility and testability.

4.6 Navigation Patterns

4.6.1 Shell (Flyout, Tabs, URI-based Nav)

Shell is MAUI’s high-level navigation abstraction. It supports:

  • Flyouts: Side menus for top

-level navigation.

  • Tabs: Bottom or top tabs for sectioned navigation.
  • URI-based Navigation: Enables deep linking and navigation by route, not just type.

Shell Example:

<Shell>
    <FlyoutItem Title="Home">
        <Tab>
            <ShellContent ContentTemplate="{DataTemplate local:HomePage}" />
        </Tab>
    </FlyoutItem>
    <TabBar>
        <Tab Title="Search">
            <ShellContent ContentTemplate="{DataTemplate local:SearchPage}" />
        </Tab>
    </TabBar>
</Shell>

Architects should use Shell for most apps, but recognize that custom navigation stacks can be plugged in where needed.

For more granular control, a custom INavigationService abstracts navigation logic away from the UI layer. This improves testability and centralizes routing.

Deep Linking: Support opening your app to a specific page from an external URI (universal links, app links).

Authentication Redirects: Handle login and post-login flows robustly, especially with OAuth or SSO providers.

4.7 Custom Handlers – When to Extend, When to Fork

4.7.1 Example: Entry Bottom-Border on iOS/Android

MAUI Handlers make it easier than ever to customize platform controls without full renderers.

Example: Customizing Entry Control

#if ANDROID
    Microsoft.Maui.Handlers.EntryHandler.Mapper.Append("Border", (handler, view) =>
    {
        handler.PlatformView.BackgroundTintList = ColorStateList.ValueOf(Android.Graphics.Color.Blue);
    });
#elif IOS
    Microsoft.Maui.Handlers.EntryHandler.Mapper.Append("Border", (handler, view) =>
    {
        handler.PlatformView.Layer.BorderColor = UIColor.Blue.CGColor;
        handler.PlatformView.Layer.BorderWidth = 2;
    });
#endif

Guidance:

  • Extend handlers for common tweaks (colors, borders, paddings).
  • Fork or create custom controls only when native features or behaviors cannot be achieved by extension.

4.8 New Controls in .NET 9+: HybridWebView, TitleBar for Desktop

HybridWebView: Offers advanced web-to-native and native-to-web bridging—enabling you to embed modern web UI (React, Angular, Blazor) within native MAUI apps. Useful for complex scenarios like dashboards, existing SPA integration, or phased migration.

TitleBar for Desktop: Full programmatic control over the window chrome on Windows and Mac. Add branded controls, search bars, or custom buttons, closing the gap between native and custom UX.

Example: TitleBar Customization

#if WINDOWS
    Microsoft.Maui.Controls.Window.Current.TitleBar.ExtendsContentIntoTitleBar = true;
    Microsoft.Maui.Controls.Window.Current.TitleBar.ButtonBackgroundColor = Colors.Transparent;
#endif

These innovations make it possible to achieve parity with platform-native experiences and push the envelope for desktop MAUI apps.


5 Integrating Native & Advanced Capabilities

Cross-platform does not mean lowest-common-denominator. High-quality apps delight users by tapping into the unique strengths of each device. .NET MAUI allows you to harness both native hardware and modern cloud-powered capabilities without sacrificing code clarity or maintainability.

5.1 MAUI Essentials (Connectivity, Geolocation, SecureStorage, Sensors)

MAUI Essentials brings together a suite of APIs that expose common device features—removing the need to write (or maintain) brittle, platform-specific shims. These abstractions cover most daily mobile requirements:

  • Connectivity: Detect network availability and type.
  • Geolocation: Access GPS and location data.
  • SecureStorage: Store sensitive data with platform security (Keychain on iOS, Keystore on Android).
  • Sensors: Access device sensors like accelerometer, gyroscope, and magnetometer.

Example: Checking Network Connectivity

using Microsoft.Maui.Networking;

if (Connectivity.Current.NetworkAccess == NetworkAccess.Internet)
{
    // Connected to internet
}

Example: Getting Location

using Microsoft.Maui.Devices.Sensors;

var location = await Geolocation.GetLastKnownLocationAsync();
if (location != null)
{
    Console.WriteLine($"Latitude: {location.Latitude}, Longitude: {location.Longitude}");
}

Example: Secure Storage

using Microsoft.Maui.Storage;

await SecureStorage.SetAsync("auth_token", token);
var storedToken = await SecureStorage.GetAsync("auth_token");

Sensors are similarly easy to use, enabling step counters, shake gestures, or orientation detection. For the majority of business and consumer apps, Essentials handles 80-90% of the hardware integration you’ll need.

5.2 The Interface-Per-Platform Pattern (INotificationService Sample)

When you need functionality not covered by Essentials, the cleanest approach is the interface-per-platform pattern. This keeps your app’s architecture solid while allowing for platform-specific implementations as needed.

Pattern in Action: Push Notification Example

  1. Define a Common Interface
public interface INotificationService
{
    void ShowNotification(string title, string message);
}
  1. Implement per Platform
  • Platforms/Android/NotificationService.cs

    public class NotificationService : INotificationService
    {
        public void ShowNotification(string title, string message)
        {
            // Android-specific notification code
        }
    }
  • Platforms/iOS/NotificationService.cs

    public class NotificationService : INotificationService
    {
        public void ShowNotification(string title, string message)
        {
            // iOS-specific notification code
        }
    }
  1. Register with DI in MauiProgram.cs
#if ANDROID
    builder.Services.AddSingleton<INotificationService, Platforms.Android.NotificationService>();
#elif IOS
    builder.Services.AddSingleton<INotificationService, Platforms.iOS.NotificationService>();
#endif
  1. Consume in Shared Code
public class MessageViewModel
{
    private readonly INotificationService _notificationService;
    public MessageViewModel(INotificationService notificationService)
    {
        _notificationService = notificationService;
    }

    public void NotifyUser(string message)
    {
        _notificationService.ShowNotification("New Alert", message);
    }
}

This pattern keeps platform-specific details out of your shared codebase, while maintaining compile-time safety and testability.

5.3 AI & ML Integration

Intelligent features increasingly differentiate modern apps. MAUI is well positioned to combine native and cloud-based AI—allowing you to offer users smarter, more personalized experiences.

5.3.1 On-Device Vision / Voice with MediaVision Plugins

On-device inference—for tasks like optical character recognition (OCR), barcode scanning, speech-to-text, or real-time translation—reduces latency and works offline. Libraries such as the .NET MAUI Community Toolkit and built-in .NET MAUI APIs allow for vision and voice tasks directly within an application . For instance, the toolkit provides controls like CameraView for accessing the device’s camera feed and MediaElement for audio and video playback. Additionally, .NET MAUI has a built-in IMediaPicker to select photos and videos from the device’s library. These tools, combined with other open-source libraries, enable developers to build rich, multimodal experiences.

Barcode Scanner Example:

using CommunityToolkit.Maui.MediaVision.BarcodeScanner;

var scanner = new BarcodeScanner();
var result = await scanner.ScanAsync();
if (result.Success)
{
    var code = result.Value.Text;
    // Do something with the code
}

Speech Recognition Example:

With a plugin like Plugin.SpeechToText or the MediaVision extensions, you can capture user input hands-free.

5.3.2 Azure AI, OpenAI, LLM Chat Experiences

Cloud AI—whether from Azure Cognitive Services, OpenAI, or other providers—opens up scenarios like:

  • Natural language chat, translation, and Q&A
  • Content moderation or summarization
  • Image classification and analysis

Typical Pattern:

  1. Gather input in the MAUI app (text, audio, photo).
  2. Call Azure/OpenAI REST APIs via an injected IAIService.
  3. Process and display AI-driven results in the UI.

Sample: Calling an Azure OpenAI Chat API

public class OpenAIService : IAIService
{
    private readonly HttpClient _httpClient;
    public OpenAIService(HttpClient httpClient)
    {
        _httpClient = httpClient;
    }

    public async Task<string> ChatAsync(string userInput)
    {
        // POST to Azure OpenAI endpoint with userInput
        // Parse and return response
    }
}

Architects should handle API keys and user privacy with care, using secure storage and secrets management (see Section 6).

5.4 Background Jobs, Push Notifications & Widgets

Background Jobs

Mobile apps frequently require background work—sync, reminders, notifications—while balancing device resources and OS restrictions.

  • Android: Use WorkManager, JobIntentService, or periodic background fetch.
  • iOS: Leverage BackgroundTasks or background fetch. Remember iOS strictly limits background execution.

MAUI exposes scheduling hooks, but for advanced use, you’ll often need platform-specific code (see the interface-per-platform pattern).

Push Notifications

Modern apps demand real-time engagement. Integrate with Firebase Cloud Messaging (FCM) for Android and Apple Push Notification Service (APNS) for iOS.

High-level pattern:

  • Register for push in platform startup.
  • Handle incoming payloads in platform-specific handlers.
  • Surface events to the shared code via DI or messaging.

Widgets

Both iOS and Android now support home screen widgets—small, glanceable app components.

  • MAUI support: Widgets require native platform code, but can be coordinated with the main app using shared services.

5.5 Secure Storage, Biometrics & Keychain / Keystore

MAUI SecureStorage provides a cross-platform abstraction, but sometimes you’ll need to tap into native APIs for advanced security.

  • Biometrics: Use Essentials’ Fingerprint APIs or platform SDKs for FaceID/TouchID (iOS) or BiometricPrompt (Android).

    var result = await Fingerprint.AuthenticateAsync("Authenticate to continue");
    if (result.Authenticated)
    {
        // Proceed with secure action
    }
  • Keychain / Keystore: SecureStorage leverages these systems, but direct access allows for more control—such as storing certificates or tokens with strict access controls.

Pattern: Abstract sensitive operations behind services, keeping security logic testable and auditable.

5.6 Offline-First Design, Local DB (SQLite, SQLitePCL.raw)

Offline resilience requires a robust local storage story. MAUI supports several approaches:

  • SQLite: The workhorse for structured local data. Use SQLite-net, Entity Framework Core (SQLite provider), or low-level SQLitePCL.raw for custom needs.

Example:

public class LocalDbContext : DbContext
{
    public DbSet<TaskItem> Tasks { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder options)
        => options.UseSqlite("Filename=app.db");
}
  • Sync: Architect sync flows to minimize conflicts, compress bandwidth, and gracefully handle errors.

  • Other storage: For simple key/value, Preferences or SecureStorage is sufficient.

Guidance: Offline-first is a user experience, not just a technical feature. Design UX cues for sync status and handle replays, failures, and partial success gracefully.


6 Quality, Security & Operations

Designing and building a sophisticated MAUI application is only the beginning. Maintaining quality, securing data, and operating at scale require deliberate architectural strategies. As with any mature app, success depends on your ability to observe, measure, and iterate.

6.1 Performance

Performance is not a feature—it’s a prerequisite for positive user experiences. Users expect instant response, smooth animations, and negligible load times.

6.1.1 UI Virtualisation, Compiled Bindings (x:DataType)

  • UI Virtualisation: Use CollectionView and its virtualization features for lists and feeds with potentially thousands of items. Avoid loading entire datasets into memory.

  • Compiled Bindings: Using x:DataType in XAML enables compile-time checking and faster bindings.

    <CollectionView ItemsSource="{Binding Items}">
        <CollectionView.ItemTemplate>
            <DataTemplate x:DataType="local:User">
                <Label Text="{Binding Name}" />
            </DataTemplate>
        </CollectionView.ItemTemplate>
    </CollectionView>

    This avoids costly reflection and improves runtime performance.

6.1.2 AOT vs JIT, Profile-Guided AOT, Trimming

  • AOT (Ahead-Of-Time) Compilation: Mandatory on iOS, optional on Android. Produces smaller, faster-starting apps at the cost of longer build times.

  • JIT (Just-In-Time): Faster builds, larger app sizes, but can delay startup. Used on Windows by default.

  • Profile-Guided AOT: Use runtime profiles to optimize which code paths are compiled ahead, balancing size and speed.

  • Trimming: Enable linker and trimming to remove unused code and reduce app size.

    Tip: Always test trimmed builds to ensure no required code is inadvertently removed.

6.1.3 Profiling Tools & Benchmarks

  • Visual Studio Profiler: Integrated for CPU, memory, and GPU profiling.
  • dotnet-counters, dotnet-trace: CLI tools for detailed telemetry.
  • BenchmarkDotNet: For micro-benchmarks and performance regression checks on algorithms or custom code.

6.2 Testing Strategy

Testing cross-platform apps is not just about preventing bugs—it’s about enabling safe, rapid delivery at scale.

6.2.1 Unit (xUnit), BDD (SpecFlow)

  • xUnit/NUnit: Write unit tests for ViewModels, services, and core logic. Focus on pure functions and DI.

  • SpecFlow: For behavior-driven development (BDD), define requirements as Gherkin scenarios and map to test code.

    Given a user is logged in
    When they open the dashboard
    Then they see their recent activity

6.2.2 UI: .NET MAUI UITest / Appium / Playwright Mobile

  • .NET MAUI UITest: Run automated UI tests on device simulators or emulators. Validate navigation, input, and visual correctness.

  • Appium: Cross-platform UI automation using WebDriver, supporting Android and iOS. Good for black-box E2E testing.

  • Playwright Mobile: Increasingly used for web-based hybrid MAUI scenarios, with robust support for automation and headless runs.

6.2.3 Service Virtualisation, Mocks, Stubs

  • Use mocking frameworks (Moq, NSubstitute) to isolate dependencies in unit/integration tests.
  • Service virtualization: For complex or third-party services, simulate APIs to ensure deterministic test results.

6.3 CI/CD

Automation is essential for quality and speed. MAUI’s single-project structure simplifies pipeline setup, but native platforms still require careful orchestration.

6.3.1 Azure DevOps / GitHub Actions Pipelines

  • Build matrix: Target Android, iOS, Windows, and macOS in parallel.
  • Hosted agents: Use Microsoft-hosted macOS and Windows runners for platform builds.
  • Artifacts: Publish signed packages (APK, IPA, MSIX) as pipeline outputs.

Example: GitHub Actions YAML

jobs:
  build-android:
    runs-on: windows-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-dotnet@v3
        with:
          dotnet-version: '9.0.x'
      - run: dotnet build MyApp.csproj -f:net9.0-android

6.3.2 Code-Signing, Provisioning Profiles, Store Automation

  • Android: Use keystore for signing APK/AABs. Automate with gradle or CLI tasks.

  • iOS: Requires valid provisioning profiles and Apple certificates. Automate with Fastlane, App Store Connect API, or Azure DevOps secure files.

  • Store Submission: Automate store submission, beta rollout (TestFlight, Google Play Internal Track), and phased releases.

6.4 Security & Compliance

Protecting user data and meeting regulatory requirements is a top architectural concern.

6.4.1 OWASP Mobile Top 10 Mitigation

Architects should regularly review the OWASP Mobile Top 10 for emerging threats:

  • Input Validation: Validate and sanitize all user input.
  • Data Storage: Never store credentials or PII in plain text.
  • Secure Communication: Enforce HTTPS with proper certificate validation.
  • Authentication/Authorization: Use proven libraries and protocols (OAuth, OIDC).

6.4.2 Secrets Management (dotnet-user-secrets, KeyVault)

  • dotnet-user-secrets: For local development, keep secrets out of source control.
  • Azure KeyVault: For production, integrate with managed secret storage and rotate credentials regularly.

Pattern: Inject secrets at runtime via environment variables or configuration providers.

6.4.3 GDPR / HIPAA / SOC 2 Considerations

  • Data minimization: Collect only what is necessary, with clear consent.
  • Right to be forgotten: Architect for data deletion and audit trails.
  • Audit Logging: Track access to sensitive operations.
  • Encryption: Encrypt sensitive data at rest and in transit.

Designing for compliance means baking privacy and security into your data model and workflows from the start.

6.5 Observability

You can’t fix what you can’t see. Instrument your app for visibility, analytics, and quick response to issues in the wild.

6.5.1 Structured Logging, Distributed Traces

  • Serilog or NLog for structured logs—correlate user actions, crashes, and network issues.
  • Distributed Tracing: Use OpenTelemetry or Application Insights to track requests from mobile UI through to backend APIs.

6.5.2 Crash & Analytics (App Center, Firebase)

  • App Center: Collects real-time crash reports, analytics, and diagnostics for MAUI apps on all platforms.
  • Firebase Analytics: Deep insights into usage patterns and engagement.

Surface critical errors quickly, and instrument custom events for business intelligence.

6.6 Release & Support Strategy (LTS, Hotfixes, Blue-Green rollout)

LTS (Long-Term Support) Planning

  • Target LTS releases of .NET for maximum stability.
  • Monitor Microsoft’s MAUI support roadmap for end-of-support dates.

Hotfixes

  • Automate patch releases through CI/CD.
  • Use feature flags to roll out or disable risky features instantly.

Blue-Green Rollout / Staged Deployment

  • Leverage app stores’ staged rollout features for gradual production exposure.
  • Use feature flags and remote config to test in production safely.

Ongoing Support

  • Maintain clear release notes and upgrade guidance.
  • Monitor dependencies for CVEs (Common Vulnerabilities and Exposures).
  • Establish SLAs for support and triage.

7 Hybrid & Alternate UI Models

The rapid evolution of UI frameworks has expanded the options available to software architects. Where pure native was once the only viable route, hybrid models are now an increasingly common and strategic choice. .NET MAUI, with its support for Blazor Hybrid and web-based controls, makes it possible to build solutions that combine the strengths of web and native technologies—enabling new approaches to code reuse, developer velocity, and platform reach.

7.1 Blazor Hybrid – When Web Devs Meet Native Containers

Blazor Hybrid is a groundbreaking model within the .NET ecosystem. It allows you to build native desktop and mobile apps using web technologies—C#, Razor, and HTML/CSS—running inside a native MAUI host. Unlike traditional web views, Blazor Hybrid apps execute C# code locally (not in the browser), bridging the world of web development and native device features.

When does Blazor Hybrid make sense?

  • You have an existing team strong in web skills, especially with Razor or Blazor WebAssembly.
  • You want to maximize code sharing across web and native apps.
  • The UI is data-driven, form-heavy, or rapidly evolving.
  • You want seamless access to device APIs while leveraging the productivity of web UI development.

Blazor Hybrid gives organizations a strategic path to reuse web UI logic, accelerate onboarding, and support unified look-and-feel across desktop and mobile, all within the .NET family.

7.2 Architecting a Blazor Hybrid Solution (Dependency Boundaries)

A successful Blazor Hybrid solution demands clear architectural boundaries. It’s easy to blur the lines between UI, shared services, and device integration—so establishing clear layers up front is crucial.

Recommended Layering:

  • Shared Business Logic: Entity models, validation, business rules—implemented in .NET Standard or .NET 6/7/8/9 class libraries.

  • UI Layer: Razor components (for Blazor Hybrid), XAML pages (for MAUI native), or both.

  • Device Services: Expose device features (GPS, camera, notifications) via dependency-injected interfaces, with implementations registered per platform in MauiProgram.cs.

Dependency Direction:

  • UI depends on business logic and device service interfaces—but not on their concrete implementations.
  • Device services should expose only what’s necessary, keeping surface areas minimal and focused.

Sample Folder Structure:

/MyApp.Core           -- Business/domain logic
/MyApp.Device         -- Device services & abstractions
/MyApp.WebUI          -- Razor components (Blazor Hybrid)
/MyApp.NativeUI       -- XAML pages (optional, for native)
/MyApp                -- MAUI host app

Tip: Avoid referencing platform-specific assemblies in your Razor class libraries. Keep everything injectable.

7.3 Code Sharing: Razor Class Libraries vs .NET MAUI Libraries

Architects need to make choices about where and how to share code between web, desktop, and mobile:

  • Razor Class Libraries (RCLs):

    • Designed for reusable UI components in Blazor.
    • Good for sharing forms, grids, custom controls, and UI helpers across Blazor Server, Blazor WebAssembly, and Blazor Hybrid (within MAUI).
    • Pure C#/Razor/HTML/CSS—no platform-specific dependencies.
  • .NET MAUI Libraries:

    • Share XAML controls, ViewModels, converters, and other UI helpers in the MAUI ecosystem.
    • Ideal for teams standardizing on XAML and MVVM for mobile and desktop.
    • Can expose abstractions for device features, dependency services, and reusable native UI.

Decision Factors:

  • If you want the widest reach and your UI is web-centric: RCLs.
  • If you’re building rich, platform-idiomatic native UIs: .NET MAUI Libraries.
  • In a hybrid solution: mix both, with clear boundaries and shared business logic underneath.

7.4 HybridWebView vs BlazorWebView: Choosing the Right Host

.NET MAUI supports two main “web in native” hosting models:

  • HybridWebView:

    • A thin, high-performance bridge for hosting arbitrary web content (static sites, SPAs, dashboards) inside a native view.
    • Allows advanced bidirectional communication between JavaScript and C#.
    • Best for integrating existing web apps, dynamic reports, or content you don’t want to fully rewrite.
  • BlazorWebView:

    • Hosts Blazor components (Razor/C#) directly inside the native app.
    • Offers full access to .NET runtime—local method calls, direct injection of device services, shared code between web and native.
    • Ideal for building “native-feeling” apps using web skills, without the limitations of the browser sandbox.

Choosing Between Them:

  • Use HybridWebView when you need to display existing, unmodified web apps or advanced JavaScript-heavy content.
  • Use BlazorWebView when building new cross-platform experiences that leverage Blazor and want the power of .NET on the client.

Sample: Adding BlazorWebView to a Page

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:blazor="clr-namespace:Microsoft.AspNetCore.Components.WebView.Maui;assembly=Microsoft.AspNetCore.Components.WebView.Maui"
             x:Class="MyApp.Pages.HybridPage">
    <blazor:BlazorWebView HostPage="wwwroot/index.html">
        <blazor:BlazorWebView.RootComponents>
            <blazor:RootComponent Selector="#app" ComponentType="{x:Type local:MainComponent}" />
        </blazor:BlazorWebView.RootComponents>
    </blazor:BlazorWebView>
</ContentPage>

7.5 PWA vs MAUI vs React-Native: Decision Matrix

Organizations often weigh .NET MAUI, Progressive Web Apps (PWA), and React Native for new projects. Each has unique strengths, tradeoffs, and ecosystem implications.

Feature.NET MAUIBlazor PWAReact Native
UI FidelityNative controlsWeb UINative controls (JS)
CodebaseC#, XAML, .NETC#, Razor, Web TechJavaScript/TypeScript
Device APIsFull (Essentials)Limited (via browser)Most (via plugins)
PerformanceNativeGood, browser-limitedNative/Hybrid
Ecosystem.NET, MSFT.NET, WebWeb, Mobile, Open
ReachAndroid, iOS, Win, MacAll browsers (no install)Android, iOS
OfflineYes (native)Yes (with Service Worker)Partial (varies)
Dev Skillset.NET/C#.NET/C#, HTML/CSSJS/TS, React

When to Choose What:

  • .NET MAUI: Need full device reach, advanced hardware access, or want unified .NET stack.
  • PWA (Blazor or Angular): Prioritize wide reach, instant access, or “zero install” distribution.
  • React Native: JavaScript-first teams, open source plugin ecosystem, or mixed native/web requirements.

Architects should map project needs and team skills before deciding—there’s no single “best” choice for all situations.


8 Patterns, Anti-Patterns & Best Practices

Good architecture is as much about what to avoid as what to embrace. MAUI offers flexibility, but without discipline, it’s easy to recreate the mistakes of the past. These patterns, anti-patterns, and checklists are distilled from real-world cross-platform experience.

8.1 Anti-Patterns: “God ViewModel”, Heavy Code-Behind, Static State

  • God ViewModel: ViewModels that handle everything—data access, navigation, business logic, UI state, validation—quickly become tangled and unmaintainable. Remedy: Apply the Single Responsibility Principle. Break large ViewModels into smaller, focused units. Delegate business logic to services.

  • Heavy Code-Behind: Placing logic in code-behind files defeats MVVM and makes testing difficult. Remedy: Use data binding, commands, and dependency injection to move logic into ViewModels or services.

  • Static State: Relying on static variables for app state can introduce hard-to-debug bugs, break navigation, and hinder testability. Remedy: Use dependency injection and appropriate service lifetimes (singleton, transient) for stateful objects.

8.2 Best Practice Checklist

  • Clean MauiProgram: Keep your app’s bootstrapping focused: configure services, handlers, fonts, and DI registration. Avoid leaking business or navigation logic into startup.

  • Reusable Components: Centralize controls, templates, and styles in shared libraries. Use DataTemplates, ControlTemplates, and custom controls to enforce consistency.

  • Desktop-Ready Layouts: Design for both touch and mouse. Use adaptive layouts (Grids, FlexLayout, adaptive breakpoints) and test with window resizing. Don’t assume all devices are phone-sized.

  • Separation of Concerns: Keep platform-specific code in /Platforms, device integrations in injectable services, and business logic in core libraries.

  • Accessibility & Localization: Wire up AutomationProperties and localize all user-facing text, even for internal or early apps.

  • Automated Testing: Cover ViewModels and services with unit tests. Invest in at least smoke UI tests for critical flows.

  • Continuous Delivery: Use CI/CD from day one, not as an afterthought.

8.3 Architecture Blueprint (Reference Solution Structure)

A clear solution structure prevents architectural drift as teams and requirements grow. Here’s a proven template for enterprise-grade MAUI solutions:

/MyApp.sln
|
|-- /src
|   |-- /MyApp.Presentation   (MAUI App, XAML, Views, Shell, Resources)
|   |-- /MyApp.Application    (Use Cases, DTOs, Orchestration)
|   |-- /MyApp.Domain         (Entities, ValueObjects, Domain Services)
|   |-- /MyApp.Infrastructure (Data Access, External APIs, Repos)
|   |-- /MyApp.Shared         (Common utilities, extensions)
|
|-- /tests
|   |-- /MyApp.UnitTests
|   |-- /MyApp.IntegrationTests
|   |-- /MyApp.UITests
|
|-- /build (CI/CD, scripts, provisioning)
|-- /docs  (Architecture decisions, diagrams)

Benefits:

  • Encourages modularity, clarity, and separation of concerns.
  • Facilitates parallel development.
  • Supports robust test coverage and easy onboarding.

8.4 Migration Path from Xamarin.Forms & Incremental Adoption

Many organizations are still running production Xamarin.Forms apps. Migrating to .NET MAUI is both an opportunity and a challenge. The migration path involves:

  • Planning: Audit current dependencies, custom renderers, third-party libraries, and platform shims.
  • Incremental Migration: .NET Upgrade Assistant (official Microsoft tool) automates much of the upgrade, but manual work will be needed—especially for custom controls and platform integrations.
  • Testing: Invest in regression and UI testing. Differences in handler implementation, layout, or rendering can lead to subtle UI bugs.
  • Re-platforming: Use the opportunity to adopt better architecture (DI, modular layering) and retire technical debt.
  • Training: Prepare teams for handler-based customization and the new project structure.

MAUI’s backward compatibility for many Xamarin.Forms patterns eases the transition, but start with non-critical flows before tackling the core of your application.


9 The Road Ahead

The .NET MAUI ecosystem is moving quickly. The decisions you make as an architect today will shape the maintainability, adaptability, and performance of your apps for years to come. The journey doesn’t end with deployment—continuous improvement and learning are now core parts of the architect’s role.

9.1 Recap of Architectural Principles & Takeaways

  • Design for reuse and separation. Keep business logic, UI, and device concerns distinct.
  • Favor dependency injection and interfaces. This promotes testability, adaptability, and clear ownership of logic.
  • Embrace modern async and resilient patterns. Users expect reliability, regardless of network or device.
  • Prioritize user experience. Adapt for form factor, input, accessibility, and localization.
  • Automate everything that matters. Quality, deployment, security, and telemetry must be baked in.
  • Avoid anti-patterns that reduce maintainability and clarity.
  • Stay current. The .NET MAUI platform is evolving. Follow releases, contribute to the community, and upgrade when strategically justified.

9.2 .NET MAUI Roadmap (.NET 10 Preview, Community Tooling)

The .NET MAUI team continues to evolve the platform rapidly, with each release bringing new features, controls, and tooling improvements:

  • .NET 10 Preview (2025):

    • Enhanced workload packaging, even faster build pipelines.
    • Expanded platform features: deeper macOS support, more desktop customization, better input support for new device types.
    • Improved Blazor Hybrid integration and cross-platform interop.
    • Continued unification with the .NET ecosystem—easier sharing with ASP.NET, Azure Functions, and microservices.
  • Community Tooling:

    • Ecosystem growth around testing, accessibility, and advanced UI controls.
    • Open-source libraries for charts, maps, and device integration continue to mature.
    • Enhanced documentation and real-world samples from both Microsoft and the community.

Stay engaged with the official MAUI roadmap and community forums. Encourage your team to share learnings and contribute improvements upstream.

9.3 Your Role as an Architect: Continuous Learning & Governance

As a software architect, your influence extends beyond technical choices:

  • Champion standards. Set and uphold coding, architectural, and delivery best practices.
  • Mentor and train. Support team members through transition, especially as you adopt new paradigms (hybrid UI, dependency injection, test automation).
  • Monitor and govern. Regularly review solutions for drift from agreed standards. Invest in automation and observability.
  • Lead continuous improvement. Run retrospectives, assess technology changes, and update practices accordingly.

Above all, stay curious. The world of cross-platform development will only accelerate—embrace change, keep learning, and help your team and business thrive in the .NET MAUI era.

Advertisement