Etiqueta: c#

DBDesigner: Creando un diseñador de bases de datos con ayuda de IA

Introducción

¡Hola nuevamente! Esta vez quiero compartirles mi experiencia utilizando inteligencia artificial para resolver una necesidad muy específica que tenía en mi trabajo. Por lo general, cuando comienzo un proyecto, prefiero diseñar la base de datos de forma visual. Esto me permite tener una mejor perspectiva de su estructura y detectar inconsistencias con mayor facilidad que revisando directamente un script SQL.

Para esta tarea normalmente utilizo Visual Paradigm en su versión Community. Sin embargo, esta edición no permite exportar scripts SQL, por lo que es necesario adquirir una licencia de pago para habilitar esta funcionalidad. La verdad es que no estaba dispuesto a pagar los 349 USD que costaba la versión más económica que permitía hacerlo, especialmente porque sería una herramienta que utilizaría exclusivamente para mi trabajo.

Hace algún tiempo descubrí que Visual Paradigm permitía exportar los diagramas en formato XML, así que desarrollé una pequeña aplicación de consola en C# que leía ese archivo y generaba el script SQL. Sin embargo, el resultado no fue tan bueno como esperaba: tenía problemas para detectar las dependencias entre tablas, especialmente cuando existían referencias circulares. Aunque solo me ocurrió una vez, era suficiente para que no pudiera confiar completamente en la herramienta.

Hace unos días decidí que podía desarrollar mi propia aplicación. Primero comencé a buscar, con ayuda de ChatGPT, controles gratuitos para Windows Forms que pudieran servirme, pero ninguno terminó de convencerme. Como tampoco tenía experiencia desarrollando controles gráficos personalizados para WinForms, pensé: “Tal vez podría utilizar Codex para crear esos controles y encargarme yo del resto”. Ya había intentado desarrollar una aplicacion, así que tenía bastante claro qué funcionalidades necesitaba y cómo quería que funcionaran.

Funciones Principales

  • Diseñar las base de datos a partir de un diagrama
  • Controles graficos “arrastrables”
  • Soporte para multiples motores de bases de datos (SQL Server, MySQL, SQLite y Firebird)
  • Guardar y abrir los proyectos generados por la misma aplicacion
  • Generar el script SQL

Funciones Secundarias

  • Deshacer/Rehacer
  • Zoom
  • Mover los puntos de conexion de la relacion
  • Crear puntos en las relaciones para modificar la trayectoria de la flecha

Primera versión: C# y Windows Forms

Abrí Codex, le compartí algunas imágenes de referencia de Visual Paradigm y traté de ser lo más claro posible al describir el tipo de controles que necesitaba. El resultado me sorprendió bastante: en muy poco tiempo había conseguido lo que para mí era la parte más difícil del proyecto, los controles gráficos. Al ver que podía avanzar tan rápido, decidí continuar y terminé construyendo una aplicación prácticamente completa.

Debo confesar que la interfaz generada por Codex no era exactamente lo que tenía en mente. Por eso, tuve que intervenir directamente en el código utilizando Visual Studio, realizar algunos ajustes visuales y agregar iconos para conseguir un resultado más cercano a lo que buscaba.

DBDesigner.NET: diseñador de diagramas de bases de datos desarrollado en C#

Se me terminaron los tokens mientras intentaba solucionar algunos problemas y me quedé a medio camino. Aunque ya había conseguido algo que me gustaba, tuve que salir de casa un rato. Antes de hacerlo, volteé a ver una MacBook Air M1 que tengo y pensé: “Si fuera multiplataforma podria continuar desde la Macbook” y todavia no terminaba de pronunciar esas palabras cuando una vocecita me dijo “¿y si la hacemos multiplataforma?”, entonces pronto descargue Codex en la Macbook y cree otra cuenta jajaj.

Kotlin y JavaFX

El primer reto fue elegir la tecnología que utilizaría. Ya había probado Codex con Blazor Server y MudBlazor, pero el resultado no terminó de convencerme, así que descarté la posibilidad de desarrollar una versión web. Entonces recordé que anteriormente había trabajado un poco con Kotlin y pensé que JavaFX podría ser una buena alternativa para crear una aplicación de escritorio multiplataforma.

Una vez más, compartí con Codex capturas de Visual Paradigm y de la aplicación que ya había desarrollado en .NET. Le expliqué cómo quería que funcionara y, cuando menos me di cuenta, ya tenía una primera versión funcional. Aunque todavía presentaba varios detalles por corregir, al igual que la versión de Windows, ya tenía una base sobre la cual continuar trabajando.

DBDesigner multiplataforma ejecutándose en macOS con Kotlin y JavaFX

Llegado a este punto, decidí crear un repositorio en GitHub. Por un lado, quería mantener un respaldo del trabajo realizado y, por otro, necesitaba poder trabajar sobre el mismo proyecto tanto en mi MacBook como en mi computadora de escritorio.

Como ya tenía la experiencia de haber desarrollado la versión para .NET, pude avanzar mucho más rápido en esta segunda implementación. Esto me permitió concentrarme en corregir los problemas que iban surgiendo e incorporar algunas funcionalidades:

Funcionalidades de DBDesigner

  • Un explorador para localizar fácilmente tablas en diagramas grandes.
  • Una sección de configuración.
  • Atajos de teclado para las principales funciones.
  • Compatibilidad con tema oscuro.
  • Un logotipo y un icono creados a partir de la combinación de otros iconos con ayuda de ChatGPT.

Mejorando la interfaz de usuario

Y sí, aunque la interfaz que generó Codex era bastante mejor que la de la primera versión en .NET, todavía no terminaba de convencerme. Así que, una vez resueltos los principales problemas de funcionamiento, decidí dedicar tiempo a mejorar su apariencia hasta conseguir un diseño con el que me sintiera más cómodo.

Conclusión

Y así fue como terminé desarrollando DBDesigner, una herramienta que espero que me resulte útil en mi trabajo diario. A partir de ahora comenzaré a utilizarla en proyectos reales para comprobar su funcionamiento, detectar posibles problemas y descubrir qué otras funcionalidades podría necesitar.

Este es apenas el comienzo del proyecto, así que seguramente habrá nuevas mejoras y características conforme vaya utilizándolo.

Arquitectura Multicapa Simplificada para aplicaciones .NET

En mi trabajo surgió la necesidad de migrar una aplicación relativamente grande que estaba construida con ASP.NET MVC 5 + DevExtreme. Yo también había participado en el desarrollo de dicha aplicación y la verdad es que conforme fue creciendo me fui dando cuenta de que tal ves el camino que habíamos elegido no era el correcto, MVC nos estaba generando controladores gigantescos incluso cuando toda la lógica de negocio estaba en la base de datos, ya que aunque usábamos repositorios para el acceso a datos, todo el sistema estaba basado en procedimientos almacenados y los repositorios solo se encargaban de llamarlos.

Entonces, al presentarse la necesidad de crear una nueva versión del mismo sistema vi que Blazor Server era una buena opción, ya que echaba de menos la característica que ofrecía WebForms de poder ordenar las vistas en carpetas y Blazor Server nos permitía hacerlo, ademas otra necesidad principal que teníamos es que el sistema nuevo debía ser responsivo y se optó por usar MudBlazor.

Cómo nació la idea de la arquitectura

Necesitaba una arquitectura que cumpliera con tres requisitos muy concretos:

  • Que fuera ordenada
  • Que fuera fácil de mantener
  • Que no fuera difícil de implementar

Así que me dedique un tiempo a investigar distintas arquitecturas de software como DDD o Core-Driven pero no me terminaban de convencer. Pero si me dieron una idea de como conseguir lo que necesitaba.

La Arquitectura Multicapa Simplificada

Supongamos que la aplicación que vamos a migrar se llama Rocket, la arquitectura se basa en cuatro capas que estarían en proyectos separados:

Rocket.Core     → Modelos, DTOs, Requests/Responses y clases compartidas
Rocket.Data → Repositorios y acceso a base de datos
Rocket.Services → Lógica de negocio y orquestación
Rocket.Web → Capa de presentación (Blazor Server)

La idea es simple, cada capa tiene una responsabilidad clara y la única capa que puede relacionarse con las demás(que de hecho debe estar relacionada) es Core.

Cómo se comunican las capas

La relación es directa y limpia:

UI (Rocket.Web)
↓
Servicios (Rocket.Services)
↓
Repositorios (Rocket.Data)
↓
Base de datos
-----------------------------
Rocket.Core → Consumido por todas las capas

Rocket.Core

Esta capa esta compuesta de al menos los siguientes namespaces

Rocket.Core
- Rocket.Core.Dtos → Clases para transferir datos
- Rocket.Core.IO → Datos de entrada y salida de los servicios

Una característica que me pareció necesaria al momento de diseñar esta arquitectura es que estuviera desacoplada de la capa de presentación, ya que anteriormente tuve la experiencia de que se había desarrollado un sistema con WebForms y después se nos solicito crear WebAPIs que expusieran algunas funcionalidades del sistema.

Por esta razón agregue un Result Wrapper que me ayude separar la capa de servicios de la capa de presentacion y en caso de ser necesario exponer funcionalidades con WebAPIs solo se consumen los servicios y ¡listo!.

Result
--------------
Success:bool → Indica si la operación fue exitosa
Message:string → Mensaje que se mostrará al usuario
Data:T (Opcional) → Objeto con la respuesta que devuelve el servicio

El código de la clase Result es el siguiente:

C#
namespace Rocket.Core.IO;

public class Result
{
    protected Result() { }
    public bool Success { get; protected set; }
    public string Message { get; protected set; }

    public static Result Ok(string message = "") =>
        new() { Success = true, Message = message };

    public static Result Fail(string message) =>
        new() { Success = false, Message = message };
}

public class Result<T>() : Result where T : BaseResponse?
{
    public T? Data { get; set; }

    public static Result<T> Ok(T data, string message = "")
        => new Result<T> { Success = true, Message = message, Data = data };

    public new static Result<T> Fail(string message)
        => new Result<T> { Success = false, Message = message, Data = default };
}

Rocket.Data

Esta capa es la encargada del acceso a datos, aquí se encuentran los repositorios que usarán los servicios junto con el ORM que se deseé utilizar, en mi caso actualmente uso Dapper pero podría ser cualquier otro, y esta capa se compone de al menos el siguiente namespace:

Rocket.Data
- Rocket.Data.Repositories → Aquí estarían todos los repositorios que requiera nuestra aplicación

Rocket.Services

Para mi esta es la capa más importante ya que aquí tendríamos las reglas de negocio, validaciones, cálculos, etc.

Esta capa estaría compuesto por al menos los siguientes namespaces:

Rocket.Services → Aquí estarían los servicios de nuestra aplicación, por ejemplo: ClientesService, PagosService, etc.

La firma típica de los métodos de cada servicio serian algo parecido a lo siguiente:

C#
public Result<TResponse> Metodo(TRequest request);

// Ejemplo
public Result<LoginResponse> Login(LoginRequest request);

De esta forma, en caso de ser necesario agregar un parámetro nuevo a la función simplemente se define una propiedad en la clase Request y se haría lo mismo para agregar un campo más en la respuesta pero en la clase Response correspondiente.

Un punto importante

Aunque ésta arquitectura permite usar interfaces para los servicios y repositorios actualmente solo agregué clases concretas ya que donde trabajo no usamos pruebas unitarias y me topé con un poco de resistencia a incluirlas.

Rocket.Web

Esta es la capa de presentación, en mi caso es una aplicación Blazor Server pero bien podría ser una aplicación MAUI o un WebAPI pero lo importante es que este proyecto no debe contener lógica de negocio, solo se encarga de la interacción del usuario con nuestra aplicación.

Conclusión

Esta arquitectura multicapa simplificada nació de una necesidad de mantener orden en un sistema relativamente grande sin caer en complejidad innecesaria. Actualmente es la forma en que estructuró las aplicaciones .NET porque ofrece un equilibrio entre claridad, simplicidad y capacidad de crecimiento.

.NET Fiddle – C# sin Visual Studio

En algunas ocasiones se necesita probar algún concepto o ver si es posible hacer algo de forma rápida.

Actualmente trabajo bastante con C# y dado que es molesto tener que crear un proyecto solo para probar algo sencillo y ejecutar el mastodonte que es Visual Studio solo para algo tan pequeño, encontré una solucion que se adapta este caso en especifico.

Se trata de dotnetfiddle.net, es un compilador online de C#, permite crear proyectos sencillos de Consola, MVC, Script y Nancy; entre sus opciones también permite seleccionar la versión de C# que queremos usar.

Esta herramienta me gusta bastante porque es bastante sencilla, permite crear snippets de código y probarlos desde el navegador, también puedes generar un enlace para compartirlo o incluso puedes trabajar al mismo tiempo sobre el mismo código con alguien más(esto último no lo he probado aún).

Para muestra un botón, sabia que existian los Extension Methods pero jamás los habia usado y me puse a revisar, ya que necesito formatear objetos DateTime a string pero quiero evitar el tener que escribir ToString(@”yyyy-MM-dd\THH:mm:ss”) cada vez que lo necesito, así que se me ocurrió intentar crear un Extension Method , así aprendia a usarlas y ademas me servia, ya que si en algun momento el formato de fecha que debo usar cambia, solo cambio el formato es la Extension Method.

Así que este fue el resultado: