ASP.NET MVC, Código Espagueti y SQL: Problemas y Soluciones

La frase "Si cree que una buena arquitectura es costosa, pruebe una arquitectura incorrecta" encapsula perfectamente la importancia de un diseño de software robusto. En el desarrollo de aplicaciones, especialmente con frameworks como ASP.NET MVC, la elección de una arquitectura adecuada es crucial para evitar problemas como el "código espagueti" y optimizar la interacción con bases de datos SQL.

Evolución de ASP.NET: De WebForms a MVC

Las aplicaciones .NET tradicionales a menudo se implementan como unidades únicas, lo que puede ser suficiente para aplicaciones internas o más pequeñas. Una aplicación monolítica, por definición, es totalmente independiente en su comportamiento principal, aunque interactúe con otros servicios o almacenes de datos.

WebForms: El Legado Monolítico

En sus inicios, Microsoft introdujo los WebForms en ASP.NET, un sistema de vistas con extensión .aspx que podía incluir un codebehind y controles. La idea era permitir a los desarrolladores crear aplicaciones web de manera similar a como lo hacían con Visual Basic para escritorio, arrastrando y soltando elementos en un editor WYSIWYG.

Sin embargo, a medida que la pericia del programador disminuía, el codebehind y el HTML de los WebForms se convertían en "código espagueti", con lógica de negocio y presentación entrelazadas. Problemas iniciales como la falta de soporte para páginas maestras llevaron a prácticas de copiar y pegar estructuras de página, aumentando la complejidad y el mantenimiento. Aunque la introducción de las MasterPages mejoró la situación, la rigidez de los controles y la dificultad para integrar librerías JavaScript dinámicas seguían siendo un desafío.

Comparación de la estructura de un proyecto WebForms y un proyecto MVC

La llegada de MVC para ASP.NET

El concepto de Model-View-Controller (MVC), creado en 1979, no era nuevo, pero su introducción en ASP.NET fue una respuesta a la necesidad de mayor flexibilidad y una mejor separación de responsabilidades. MVC en ASP.NET se caracteriza por:

  • Controladores: Reciben todas las peticiones, reemplazando a los genéricos .ashx, .asmx o .aspx. Sus métodos por defecto son acciones del controlador.
  • Vistas: Son puramente para la presentación, utilizando sintaxis WebForm en MVC 1 y 2, y Razor en MVC 3 y 4.
  • Modelos: Clases con propiedades multifunción, capaces de validar datos automáticamente, enviar y recibir formularios, y sincronizar datos con la base de datos (por ejemplo, con Entity Framework Code First).
  • Proyecto: Se compila en un binario cerrado, lo que mejora la gestión y el rendimiento.

Razor: El nuevo motor de plantillas

Razor es el motor de plantillas que revolucionó la creación de vistas en ASP.NET MVC. Quienes programaron con WebForms recordarán la complejidad sintáctica con las etiquetas <% codigo %> y <%=codigo%>. Razor simplifica drásticamente la inclusión de código en HTML, haciendo las vistas más limpias y legibles.

C# Razor Pages - Tutorial completo

Aquí se muestra un ejemplo simplificado de la diferencia entre Razor y WebForms para la inclusión de código:

 <!-- Ejemplo en Razor --> <div>Hola, @Model.Nombre</div> <!-- Ejemplo en WebForms --> <div>Hola, <%= Model.Nombre %></div> 

Además, en Razor, las MasterPages se conocen como Layouts, y los controles de WebForms son ahora vistas parciales, pero sin el codebehind.

Manejo de la complejidad en aplicaciones monolíticas

A medida que las aplicaciones crecen en complejidad, una forma de gestionarla es dividiéndola según sus responsabilidades. Este enfoque sigue el principio de separación de preocupaciones y ayuda a mantener el código base organizado. La organización del código en capas permite reutilizar la funcionalidad común de bajo nivel y aplicar restricciones sobre cómo las capas interactúan entre sí, lo que facilita la encapsulación y la sustitución de funcionalidades. Por ejemplo, una aplicación podría cambiar de una base de datos SQL Server a una solución basada en la nube o una API web.

Arquitectura por capas tradicional

Una arquitectura común es la de tres capas: UI (interfaz de usuario), BLL (capa de lógica de negocios) y DAL (capa de acceso a datos). En este modelo, los usuarios interactúan con la capa de UI, que solo se comunica con la BLL. La BLL, a su vez, puede llamar a la DAL para las solicitudes de acceso a datos. La UI no debe interactuar directamente con la DAL.

Diagrama de arquitectura por capas tradicional (UI, BLL, DAL)

Una desventaja de este enfoque es que las dependencias en tiempo de compilación fluyen de arriba hacia abajo (UI -> BLL -> DAL). Esto significa que la BLL, que contiene la lógica más importante, depende de los detalles de implementación del acceso a datos, lo que dificulta las pruebas unitarias de la lógica de negocio, ya que a menudo requieren una base de datos de prueba.

Arquitectura Limpia (Clean Architecture)

La arquitectura limpia, también conocida como Arquitectura Hexagonal, Puertos y Adaptadores, o Arquitectura Cebolla, invierte la dirección de las dependencias. La lógica de negocio y el modelo de aplicación se encuentran en el centro. En lugar de que la lógica de negocio dependa del acceso a los datos o de otras consideraciones de infraestructura, esta dependencia se invierte: ahora los detalles de la infraestructura y la implementación dependen del núcleo de la aplicación. Esto se logra definiendo abstracciones (interfaces) en el Application Core, que luego son implementadas por tipos definidos en la capa de infraestructura.

Diagrama de Arquitectura Limpia (círculos concéntricos)

En este diagrama, las dependencias fluyen hacia el círculo más interno, el Application Core, que no tiene dependencias de otras capas de aplicación. Las entidades e interfaces de la aplicación residen en el centro, y los servicios de dominio implementan interfaces definidas en el círculo interno.

Con la arquitectura limpia, la capa de UI trabaja con interfaces definidas en el Application Core en tiempo de compilación y no necesita conocer los tipos de implementación definidos en la capa de infraestructura. Esto facilita la escritura de pruebas unitarias automatizadas para el Application Core, ya que no depende de la infraestructura, y también simplifica el intercambio de implementaciones.

Relación de dependencias en la Arquitectura Limpia

En una solución de arquitectura limpia, cada proyecto tiene responsabilidades claras:

  • Application Core: Contiene el modelo de negocio (entidades, servicios, interfaces). Estas interfaces incluyen abstracciones para operaciones que realizará la infraestructura, como acceso a datos, acceso al sistema de archivos o llamadas de red.
  • Infraestructura: Incluye implementaciones de acceso a datos (como Entity Framework DbContext, objetos de EF Core Migration) y otras implementaciones de servicios que interactúan con la infraestructura.
  • Capa de UI: Es el punto de entrada de la aplicación. Hace referencia al proyecto de Application Core y sus tipos interactúan con la infraestructura estrictamente a través de interfaces definidas en Application Core. La clase Startup o el archivo Program.cs son responsables de configurar la aplicación y conectar tipos de implementación a interfaces.

Despliegue y escalabilidad

Las aplicaciones monolíticas se pueden desplegar como un único contenedor. Si bien la aplicación internamente puede estar organizada en capas o bibliotecas, se despliega como una unidad. Para escalar, simplemente se añaden más copias con un balanceador de carga.

Despliegue monolítico con contenedores

Sin embargo, la desventaja de este enfoque surge cuando la aplicación crece y requiere escalabilidad. En un sistema de comercio electrónico, por ejemplo, el componente de información del producto probablemente necesitará escalar mucho más que el de gestión de pedidos o de empleados. Si bien la clonación completa de la instancia monolítica puede funcionar inicialmente, no siempre es la solución más eficiente o rentable a largo plazo.

Escalabilidad en Azure

En Microsoft Azure, las aplicaciones monolíticas pueden implementarse mediante máquinas virtuales dedicadas o Azure App Services. Azure App Services puede ejecutar aplicaciones monolíticas y escalar fácilmente instancias sin la necesidad de administrar máquinas virtuales. También puede ejecutar instancias únicas de contenedores Docker, lo que simplifica la implementación. Los contenedores ofrecen ventajas en el despliegue de aplicaciones monolíticas, ya que el escalado de instancias es mucho más rápido y sencillo que la implementación de máquinas virtuales adicionales, y las actualizaciones como imágenes Docker son más eficientes.

Opciones de escalabilidad en Azure para aplicaciones monolíticas

ASP Classic: Mantenimiento y Modernización

A pesar de ser considerado un lenguaje "antiguo", ASP Classic sigue siendo una plataforma potente y confiable que puede satisfacer el 95% de lo que la web moderna ofrece. Para muchas organizaciones, mantener y evolucionar sus aplicaciones ASP Classic es una opción más rentable que una reescritura completa, evitando interrupciones y errores.

Capacidades de ASP Classic en la actualidad

ASP Classic, con una base de conocimiento profundo en IIS 7/8/10, VBScript, JScript, Javascript, CSS 4 y HTML 4/5, permite integrar tecnologías modernas y ofrecer funcionalidades avanzadas. Algunas de estas incluyen:

  • Gestión de bases de datos documentales y fotográficas.
  • Generación automática de documentos e imágenes (PNG, JPG, TIF, WEBP, PDF, PSD) en alta definición.
  • Generación de códigos QR.
  • Adición de datos estructurados JSON-LD a páginas web.
  • Comercio electrónico e integración con PayPal Express Checkout.
  • Interacciones nativas con servidores LDAP Active Directory.
  • Generación automatizada de flujos RSS, mapas de sitio y documentos XML.
  • Informes de datos y creación de paneles de control.
  • Sistema de autenticación/registro integrado en AJAX + JSON + ASP.
  • Gestión de metadatos EXIF y etiquetas IPTC.
  • Importación/exportación de archivos .XLS/.XLSX en línea.
  • Interfaz con objetos a través de WMI.
  • Módulos de gestión de blogs y noticias.
  • Sistema de planificación, calendario y reservas en línea.
  • Funciones avanzadas de correo electrónico y correo masivo.
  • Envío y recepción de SMS.
  • Creación de promociones y cupones de descuento.
  • Implementación de cuestionarios y encuestas de satisfacción.

Es erróneo pensar que ASP Classic no permite obtener UI/UX modernas. Gracias a tecnologías punteras como Ajax, JSON, WEBP, SVG, CSS4 y Responsiveness, es perfectamente capaz de ofrecer experiencias de usuario agradables y racionales.

Manejo de caracteres y bases de datos

El tratamiento de caracteres internacionales y signos diacríticos en ASP Classic puede ser complejo. Sin embargo, con un conocimiento sólido, es posible gestionar varios juegos de caracteres, siendo UTF-8 el preferido para manejar caracteres internacionales de forma fiable. Para bases de datos MySQL o MariaDB, la programación UTF8_UNICODE_CI o LATIN1 no es totalmente compatible con el estándar UTF8. La conversión a UTF8_MB4 permite almacenar de forma fiable un mayor número de caracteres, incluyendo emojis.

El poder de Javascript en ASP Classic

Mucho antes de Node.js, Microsoft puso el poder de Javascript a disposición de los desarrolladores del lado del servidor. Aunque la mayoría de los scripts ASP Classic están en VBScript, JScript (derivado de JavaScript) está integrado y es compatible con IIS. Es posible diseñar scripts avanzados que interactúen con VBScript y JScript en la misma página ASP, aprovechando JScript para procesar datos JSON, interactuar con APIs de terceros o manejar datos AJAX, complementando las limitaciones de VBScript.

Problemas comunes en ASP.NET y sus soluciones

Error "La operación no es válida debido al estado actual del objeto" (MaxHttpCollectionKeys)

En ASP.NET, al enviar formularios con un número muy elevado de elementos, puede aparecer el error "La operación no es válida debido al estado actual del objeto" (o "Operation is not valid due to the current state of the object" en inglés). Este problema se resuelve aumentando el valor de la clave MaxHttpCollectionKeys en el archivo web.config, dentro de la sección <appSettings>. Un valor de 5000 suele ser suficiente.

 <configuration> <appSettings> <add key="MaxHttpCollectionKeys" value="5000"/> </appSettings> <!-- ... otros ajustes --> </configuration> 

Ordenación de Bundles en ASP.NET MVC

Al utilizar Bundles (ScriptBundle y StyleBundle) en ASP.NET MVC para agrupar y minimizar archivos JavaScript y CSS, es común que el orden de carga no sea el deseado, lo que puede causar errores si las dependencias no se satisfacen (por ejemplo, Backbone requiere Underscore). Para solucionar esto, se puede crear una clase personalizada que implemente la interfaz IBundleOrderer. Esta clase permite controlar el orden en que se incluyen los archivos en la página, asegurando que se carguen en la secuencia correcta.

 public class UserDefinedBundleOrderer : IBundleOrderer { public IEnumerable<FileInfo> OrderFiles(BundleContext context, IEnumerable<FileInfo> files) { return files; } } 

Luego, se asigna esta clase al Orderer del bundle:

 myBundle.Orderer = new UserDefinedBundleOrderer(); 

Esto permite mantener el orden definido al incluir los archivos, solucionando problemas de dependencia entre scripts o estilos.

tags: #mvc #espagueti #sql