En proyectos reales, la forma en que se estructura una solución WinUI 3 determina en gran medida su capacidad de evolucionar sin degradar la calidad del código. Una estructura deficiente no suele fallar en las primeras semanas, pero introduce fricción constante a medida que el sistema crece.
Cuando la aplicación incorpora nuevas pantallas, servicios y reglas de negocio, la falta de organización clara se traduce en dependencias cruzadas, duplicación de lógica y dificultades para realizar cambios sin efectos colaterales.
Este artículo presenta una estructura de proyecto para WinUI 3 orientada a mantenibilidad y escalabilidad, con un enfoque práctico y alineado a escenarios de producción.
El problema Link to heading
Las plantillas iniciales de proyectos WinUI 3 son útiles para comenzar, pero no imponen una estructura sólida. Es habitual encontrar soluciones con:
- Lógica distribuida en code-behind
- ViewModels sin responsabilidades claras
- Servicios mezclados con lógica de presentación
- Falta de separación por capas
- Proyectos monolíticos difíciles de modularizar
Ejemplo de estructura problemática Link to heading
/App
/MainWindow.xaml
/MainWindow.xaml.cs
/Helpers
/Utils
/Services
Problemas:
- Carpetas genéricas sin propósito claro
- Dependencias implícitas
- Difícil navegación del código
- Baja cohesión
En equipos, este tipo de estructura genera inconsistencias y aumenta el costo de mantenimiento.
La solución Link to heading
Una estructura efectiva debe:
- Separar responsabilidades por capas
- Facilitar la navegación del código
- Permitir crecimiento modular
- Reducir acoplamientos innecesarios
Se propone una organización por capas y dominios.
Paso 1: Estructura base recomendada Link to heading
/App
/Views
/ViewModels
/Models
/Services
/Infrastructure
/Resources
Descripción de cada capa Link to heading
- App: inicialización, configuración global
- Views: XAML y code-behind mínimo
- ViewModels: lógica de presentación y estado
- Models: entidades de dominio
- Services: acceso a datos, APIs, lógica externa
- Infrastructure: logging, configuración, utilidades técnicas
- Resources: estilos, temas, assets
Esta separación permite entender rápidamente el propósito de cada componente.
Paso 2: Organización por características (feature folders) Link to heading
En aplicaciones medianas o grandes, se recomienda agrupar por funcionalidad:
/Features
/Customers
CustomerView.xaml
CustomerViewModel.cs
CustomerService.cs
/Orders
OrderView.xaml
OrderViewModel.cs
OrderService.cs
Ventajas:
- Alta cohesión por dominio
- Reducción de dependencias cruzadas
- Facilita trabajo en paralelo
Paso 3: Definición clara de contratos Link to heading
Los servicios deben exponerse mediante interfaces.
public interface ICustomerService
{
Task<List<Customer>> GetCustomersAsync();
}
public class CustomerService : ICustomerService
{
public async Task<List<Customer>> GetCustomersAsync()
{
// Simulación de acceso a datos
await Task.Delay(300);
return new List<Customer>
{
new Customer { Name = "Cliente 1" },
new Customer { Name = "Cliente 2" }
};
}
}
Esto permite desacoplar implementación y consumo.
Paso 4: ViewModels enfocados Link to heading
Cada ViewModel debe tener una única responsabilidad.
using System.Collections.ObjectModel;
public class CustomerViewModel
{
private readonly ICustomerService _service;
public ObservableCollection<Customer> Customers { get; } = new();
public CustomerViewModel(ICustomerService service)
{
_service = service;
}
public async Task LoadAsync()
{
var data = await _service.GetCustomersAsync();
Customers.Clear();
foreach (var item in data)
{
Customers.Add(item);
}
}
}
Características:
- Sin lógica de UI
- Dependencias explícitas
- Preparado para testing
Paso 5: Configuración de dependencias Link to heading
Incluso si se comienza de forma simple, es recomendable preparar la solución para inyección de dependencias.
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddApplicationServices(this IServiceCollection services)
{
services.AddSingleton<ICustomerService, CustomerService>();
services.AddTransient<CustomerViewModel>();
return services;
}
}
Inicialización:
var services = new ServiceCollection();
services.AddApplicationServices();
var provider = services.BuildServiceProvider();
Esto permite evolucionar hacia una arquitectura más robusta sin refactorizaciones masivas.
Paso 6: Recursos y estilos centralizados Link to heading
Los estilos deben definirse en un lugar único.
<Application.Resources>
<ResourceDictionary>
<Style TargetType="Button">
<Setter Property="Padding" Value="10"/>
</Style>
</ResourceDictionary>
</Application.Resources>
Evita duplicación y mejora consistencia visual.
Buenas prácticas Link to heading
- Evitar carpetas genéricas sin propósito
- Mantener responsabilidades claras por capa
- Utilizar interfaces para servicios
- Preparar la solución para modularidad
- Centralizar recursos y configuraciones
Conclusión Link to heading
La estructura de un proyecto WinUI 3 no es un detalle organizativo, sino una decisión arquitectónica que impacta directamente en la calidad del software. Una estructura clara reduce complejidad, mejora la colaboración y permite escalar la aplicación sin introducir deuda técnica.
Adoptar una organización por capas y, cuando corresponde, por características, facilita el mantenimiento y prepara el sistema para evolucionar de forma controlada.