When developing a software application, choosing the right architectural pattern is crucial for maintainability, scalability, and testability. Let's explore the five popular architectural patterns: MVC, MVP, MVI, MVVM, and VIPER, with examples in C#. We will discuss what they are, how they differ, and how to determine the right one for your project.

1. MVC (Model-View-Controller)

What is MVC?

MVC is an architectural pattern that separates an application into three main components.

// Model
public class UserModel
{
    public string Username { get; set; }
    public string Password { get; set; }
}

// View
public class UserView
{
    public void DisplayUserDetails(UserModel user)
    {
        Console.WriteLine($"Username: {user.Username}");
    }
}

// Controller
public class UserController
{
    private readonly UserModel _user;
    private readonly UserView _view;

    public UserController(UserModel user, UserView view)
    {
        _user = user;
        _view = view;
    }

    public void SetUserDetails(string username, string password)
    {
        _user.Username = username;
        _user.Password = password;
    }

    public void UpdateView()
    {
        _view.DisplayUserDetails(_user);
    }
}

// Usage
var user = new UserModel();
var view = new UserView();
var controller = new UserController(user, view);

controller.SetUserDetails("john_doe", "password123");
controller.UpdateView();

2. MVP (Model-View-Presenter)

What is MVP?

MVP is similar to MVC but with a distinct difference: the presenter handles all the logic to update the view. This makes the view passive, focusing solely on UI representation.

// Model
public class UserModel
{
    public string Username { get; set; }
    public string Password { get; set; }
}

// View Interface
public interface IUserView
{
    void DisplayUserDetails(UserModel user);
}

// View
public class UserView : IUserView
{
    public void DisplayUserDetails(UserModel user)
    {
        Console.WriteLine($"Username: {user.Username}");
    }
}

// Presenter
public class UserPresenter
{
    private readonly UserModel _user;
    private readonly IUserView _view;

    public UserPresenter(UserModel user, IUserView view)
    {
        _user = user;
        _view = view;
    }

    public void SetUserDetails(string username, string password)
    {
        _user.Username = username;
        _user.Password = password;
    }

    public void UpdateView()
    {
        _view.DisplayUserDetails(_user);
    }
}

// Usage
var user = new UserModel();
var view = new UserView();
var presenter = new UserPresenter(user, view);

presenter.SetUserDetails("john_doe", "password123");
presenter.UpdateView();

3. MVI (Model-View-Intent)

What is MVI?

MVI is a reactive pattern where.

// Model
public class UserModel
{
    public string Username { get; set; }
    public string Password { get; set; }
}

// View Interface
public interface IUserView
{
    void DisplayUserDetails(UserModel user);
    void DisplayError(string message);
}

// Intent
public class UserIntent
{
    public Action<string, string> OnUserDetailsSubmitted;
}

// View
public class UserView : IUserView
{
    public void DisplayUserDetails(UserModel user)
    {
        Console.WriteLine($"Username: {user.Username}");
    }

    public void DisplayError(string message)
    {
        Console.WriteLine($"Error: {message}");
    }
}

// Usage
var user = new UserModel();
var view = new UserView();
var intent = new UserIntent();

intent.OnUserDetailsSubmitted += (username, password) =>
{
    if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
    {
        view.DisplayError("Invalid input");
    }
    else
    {
        user.Username = username;
        user.Password = password;
        view.DisplayUserDetails(user);
    }
};

intent.OnUserDetailsSubmitted("john_doe", "password123");

4. MVVM (Model-View-ViewModel)

What is MVVM?

MVVM is widely used in WPF and Xamarin applications. It separates.

// Model
public class UserModel
{
    public string Username { get; set; }
    public string Password { get; set; }
}

// ViewModel
public class UserViewModel : INotifyPropertyChanged
{
    private UserModel _user;

    public UserViewModel()
    {
        _user = new UserModel();
    }

    public string Username
    {
        get => _user.Username;
        set
        {
            if (_user.Username != value)
            {
                _user.Username = value;
                OnPropertyChanged(nameof(Username));
            }
        }
    }

    public string Password
    {
        get => _user.Password;
        set
        {
            if (_user.Password != value)
            {
                _user.Password = value;
                OnPropertyChanged(nameof(Password));
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

// View (XAML)
/*
<UserControl x:Class="UserView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <StackPanel>
        <TextBox Text="{Binding Username, UpdateSourceTrigger=PropertyChanged}" />
        <TextBox Text="{Binding Password, UpdateSourceTrigger=PropertyChanged}" />
    </StackPanel>
</UserControl>
*/

// Usage in code-behind
public partial class UserView : UserControl
{
    public UserView()
    {
        InitializeComponent();
        DataContext = new UserViewModel();
    }
}

5. VIPER (View, Interactor, Presenter, Entity, Router)

What is VIPER?

VIPER is an architectural pattern used mainly in iOS development but can be adapted to other platforms. It separates responsibilities into.

Due to the complexity and the common use in mobile development, a full example in C# would be quite lengthy. However, here’s a brief outline.

// View Interface
public interface IUserView
{
    void DisplayUserDetails(UserModel user);
}

// Interactor Interface
public interface IUserInteractor
{
    void FetchUserDetails(Action<UserModel> callback);
}

// Presenter
public class UserPresenter
{
    private readonly IUserView _view;
    private readonly IUserInteractor _interactor;

    public UserPresenter(IUserView view, IUserInteractor interactor)
    {
        _view = view;
        _interactor = interactor;
    }

    public void GetUserDetails()
    {
        _interactor.FetchUserDetails(user => _view.DisplayUserDetails(user));
    }
}

// Router Interface
public interface IUserRouter
{
    void NavigateToUserDetails();
}

// Usage
public class UserView : IUserView
{
    public void DisplayUserDetails(UserModel user)
    {
        Console.WriteLine($"Username: {user.Username}");
    }
}

public class UserInteractor : IUserInteractor
{
    public void FetchUserDetails(Action<UserModel> callback)
    {
        // Fetch user details (e.g., from a database)
        var user = new UserModel { Username = "john_doe", Password = "password123" };
        callback(user);
    }
}

public class UserRouter : IUserRouter
{
    public void NavigateToUserDetails()
    {
        Console.WriteLine("Navigating to user details view...");
    }
}

// Initial setup
var view = new UserView();
var interactor = new UserInteractor();
var presenter = new UserPresenter(view, interactor);
var router = new UserRouter();

presenter.GetUserDetails();
router.NavigateToUserDetails();

Conclusion

Choosing the right architectural pattern depends on your project's requirements, complexity, and the team's familiarity with the pattern. Here are some general guidelines:

Each pattern has its strengths and is best suited for specific scenarios. Understanding these patterns and their appropriate use cases is crucial for building robust and maintainable applications.