Browse Source

chore: migrate to dotnet 10

Mariano Z. 7 hours ago
parent
commit
266415acaa

+ 1 - 0
.tool-versions

@@ -0,0 +1 @@
+dotnet 10.0.100

+ 2 - 2
Dockerfile

@@ -1,5 +1,5 @@
 # Use the official .NET SDK image as a build stage
-FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
+FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
 WORKDIR /app
 
 # Copy the solution and global.json to the container
@@ -19,7 +19,7 @@ WORKDIR /app/Products.Backoffice
 RUN dotnet publish -c Release -o out
 
 # Use the official .NET runtime image as the final base image
-FROM mcr.microsoft.com/dotnet/aspnet:9.0
+FROM mcr.microsoft.com/dotnet/aspnet:10.0
 WORKDIR /app
 COPY --from=build /app/Products.Backoffice/out ./
 

+ 4 - 5
Products.API/Products.API.csproj

@@ -1,16 +1,15 @@
 <Project Sdk="Microsoft.NET.Sdk.Web">
 
     <PropertyGroup>
-        <TargetFramework>net9.0</TargetFramework>
+        <TargetFramework>net10.0</TargetFramework>
         <Nullable>enable</Nullable>
         <ImplicitUsings>enable</ImplicitUsings>
     </PropertyGroup>
 
     <ItemGroup>
-        <PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="9.0.4" />
-        <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.4" />
-        <PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="9.0.0" />
-        <PackageReference Include="Swashbuckle.AspNetCore" Version="8.1.0" />
+        <PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="10.0.0" />
+        <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.0" />
+        <PackageReference Include="Scalar.AspNetCore" Version="2.6.0" />
     </ItemGroup>
 
 

+ 4 - 5
Products.API/Program.cs

@@ -1,4 +1,5 @@
 using Products.API.Middlewares;
+using Scalar.AspNetCore;
 using Products.Business.Persistence;
 using Products.Business.Repository;
 using Products.Business.Service;
@@ -15,9 +16,7 @@ internal class Program
 // Add services to the container.
 
         builder.Services.AddControllers().AddNewtonsoftJson();
-        // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
-        builder.Services.AddEndpointsApiExplorer();
-        builder.Services.AddSwaggerGen();
+        builder.Services.AddOpenApi();
         builder.Services.AddScoped<IProductService, ProductService>();
         builder.Services.AddScoped<IProductRepository, ProductRepository>();
         builder.Services.AddDbContext<DataContext>();
@@ -41,8 +40,8 @@ internal class Program
 // Configure the HTTP request pipeline.
         if (app.Environment.IsDevelopment())
         {
-            app.UseSwagger();
-            app.UseSwaggerUI();
+            app.MapOpenApi();
+            app.MapScalarApiReference();
         }
 
         app.UseHttpsRedirection();

+ 1 - 1
Products.Backoffice/Models/ErrorViewModel.cs

@@ -3,5 +3,5 @@ namespace Products.Backoffice.Models;
 public class ErrorViewModel 
 {     
     public int StatusCode { get; set; }     
-    public string OriginalPath { get; set; } 
+    public string? OriginalPath { get; set; } 
 } 

+ 1 - 1
Products.Backoffice/Products.Backoffice.csproj

@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk.Web">
 
     <PropertyGroup>
-        <TargetFramework>net9.0</TargetFramework>
+        <TargetFramework>net10.0</TargetFramework>
         <Nullable>enable</Nullable>
         <ImplicitUsings>enable</ImplicitUsings>
     </PropertyGroup>

+ 3 - 3
Products.Business/Products.Business.csproj

@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
     <PropertyGroup>
-        <TargetFramework>net9.0</TargetFramework>
+        <TargetFramework>net10.0</TargetFramework>
         <ImplicitUsings>enable</ImplicitUsings>
         <Nullable>enable</Nullable>
     </PropertyGroup>
@@ -11,8 +11,8 @@
     </ItemGroup>
 
     <ItemGroup>
-        <PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="9.0.4" />
-        <PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="9.0.4" />
+        <PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="10.0.0" />
+        <PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="10.0.0" />
     </ItemGroup>
 
 </Project>

+ 2 - 2
Products.Common/Dtos/CreateProductDto.cs

@@ -6,11 +6,11 @@ namespace Products.Common.Dtos;
 public class CreateProductDto
 {
     [Required]
-    public string Name { get; set; }
+    public required string Name { get; set; }
     
     [Required]
     public Color Color { get; set; }
     
     [Required]
-    public string Brand { get; set; }
+    public required string Brand { get; set; }
 }

+ 6 - 1
Products.Common/Products.Common.csproj

@@ -1,8 +1,13 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
     <PropertyGroup>
-        <TargetFramework>net9.0</TargetFramework>
+        <TargetFramework>net10.0</TargetFramework>
         <ImplicitUsings>enable</ImplicitUsings>
         <Nullable>enable</Nullable>
     </PropertyGroup>
+
+    <ItemGroup>
+      <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
+    </ItemGroup>
+
 </Project>

+ 1 - 1
Products.Frontoffice/.tool-versions

@@ -1 +1 @@
-nodejs 20.3.1
+nodejs 22.14.0

File diff suppressed because it is too large
+ 464 - 613
Products.Frontoffice/package-lock.json


+ 8 - 8
Products.Frontoffice/package.json

@@ -10,19 +10,19 @@
 		"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch"
 	},
 	"devDependencies": {
-		"@sveltejs/adapter-auto": "^2.0.0",
-		"@sveltejs/kit": "^1.20.4",
+		"@sveltejs/adapter-auto": "^3.0.0",
+		"@sveltejs/kit": "^2.0.0",
 		"prettier": "3.0.3",
-		"prettier-plugin-svelte": "3.0.3",
-		"svelte": "^4.0.5",
-		"svelte-check": "^3.4.3",
+		"prettier-plugin-svelte": "^3.3.0",
+		"svelte": "^5.0.0",
+		"svelte-check": "^4.0.0",
 		"tslib": "^2.4.1",
 		"typescript": "^5.0.0",
-		"vite": "^4.4.2"
+		"vite": "^8.0.0"
 	},
 	"type": "module",
 	"dependencies": {
-		"axios": "1.5.0",
-		"zod": "3.22.2"
+		"axios": "^1.7.0",
+		"zod": "^3.23.0"
 	}
 }

+ 16 - 21
Products.Frontoffice/src/routes/+page.svelte

@@ -5,14 +5,11 @@
   import Table from "./table.svelte";
   import ProductForm from "./upsert-product-form.svelte";
 
-  let showPopup = false;
-  let product: Product;
+  let showPopup = $state(false);
+  let product = $state<Product>({ name: "", brand: "" });
 
   const resetProduct = () => {
-    product = {
-      name: "",
-      brand: "",
-    };
+    product = { name: "", brand: "" };
   };
 
   const closeModal = () => {
@@ -25,19 +22,18 @@
     } else {
       createProduct(product);
     }
-    showPopup = false;
     closeModal();
   };
 
-  const handleEdit = (event: CustomEvent<Product>) => {
-    product = event.detail;
+  const handleEdit = (p: Product) => {
+    product = p;
     showPopup = true;
   };
 </script>
 
 <h1>Welcome to SvelteKit</h1>
 <p>
-  Visit <a href="https://kit.svelte.dev">kit.svelte.dev</a> to read the documentation
+  Visit <a href="https://svelte.dev/docs/kit">svelte.dev/docs/kit</a> to read the documentation
 </p>
 
 {#await getAllProducts()}
@@ -48,7 +44,7 @@
       <button
         type="button"
         class="btn btn-success"
-        on:click={() => {
+        onclick={() => {
           showPopup = true;
           resetProduct();
         }}
@@ -57,17 +53,16 @@
       </button>
     </div>
   </div>
-  <Table on:edit={handleEdit} />
+  <Table onedit={handleEdit} />
   <Modal open={showPopup} onClose={closeModal}>
-    <h5 class="modal-title" id="sampleModalLabel" slot="title">
-      {product.id ? "Edit Product" : "Add Product"}
-    </h5>
-    <ProductForm
-      {product}
-      on:save={upsertProduct}
-      on:dismiss={() => closeModal()}
-      slot="body"
-    />
+    {#snippet title()}
+      <h5 class="modal-title" id="sampleModalLabel">
+        {product.id ? "Edit Product" : "Add Product"}
+      </h5>
+    {/snippet}
+    {#snippet body()}
+      <ProductForm bind:product onsave={upsertProduct} ondismiss={closeModal} />
+    {/snippet}
   </Modal>
 {:catch error}
   <p style="color: red">{error.message}</p>

+ 17 - 12
Products.Frontoffice/src/routes/modal.svelte

@@ -1,17 +1,20 @@
 <script lang="ts">
+  import type { Snippet } from "svelte";
   import { fade, fly } from "svelte/transition";
   import { quintOut } from "svelte/easing";
 
-  export let open = false;
-  export let showBackdrop = true;
+  interface Props {
+    open: boolean;
+    showBackdrop?: boolean;
+    onClose?: () => void;
+    title?: Snippet;
+    body?: Snippet;
+  }
 
-  export let onClose = () => {};
+  let { open, showBackdrop = true, onClose = () => {}, title, body }: Props = $props();
 
   const modalClose = () => {
-    open = false;
-    if (onClose) {
-      onClose();
-    }
+    onClose();
   };
 </script>
 
@@ -32,24 +35,26 @@
     >
       <div class="modal-content">
         <div class="modal-header">
-          <slot name="title">
+          {#if title}
+            {@render title()}
+          {:else}
             <h5 class="modal-title" id="modalTitle">Default Modal Title</h5>
-          </slot>
+          {/if}
           <button
             type="button"
             class="btn-close"
-            on:click={modalClose}
+            onclick={modalClose}
             aria-label="Close"
           ></button>
         </div>
         <div class="modal-body">
-          <slot name="body" />
+          {@render body?.()}
         </div>
       </div>
     </div>
   </div>
   {#if showBackdrop}
-    <div class="modal-backdrop show" transition:fade={{ duration: 150 }} />
+    <div class="modal-backdrop show" transition:fade={{ duration: 150 }}></div>
   {/if}
 {/if}
 

+ 4 - 5
Products.Frontoffice/src/routes/table.svelte

@@ -3,13 +3,12 @@
 
   import { deleteProduct } from "$lib/api";
   import { products } from "$lib/store";
-  import { createEventDispatcher } from "svelte";
 
-  const dispatch = createEventDispatcher<{ edit: Product }>();
+  let { onedit }: { onedit: (product: Product) => void } = $props();
 
   const editProduct = (product: Product) => {
     if (product) {
-      dispatch("edit", product);
+      onedit(product);
     }
   };
 </script>
@@ -41,12 +40,12 @@
           >
           <td>
             <button
-              on:click={() => editProduct(product)}
+              onclick={() => editProduct(product)}
               class="btn btn-primary"
               data-bs-toggle="modal">Edit</button
             >
             <button
-              on:click={() => deleteProduct(product.id)}
+              onclick={() => deleteProduct(product.id)}
               class="btn btn-danger">Delete</button
             >
           </td>

+ 13 - 39
Products.Frontoffice/src/routes/upsert-product-form.svelte

@@ -6,46 +6,20 @@
     ProductSchema,
     ColorKeys,
   } from "$lib";
-  import { createEventDispatcher } from "svelte";
-  import type { ZodFormattedError } from "zod";
 
-  const dispatchSave = createEventDispatcher<{ save: Product }>();
-  const dispatchDismiss = createEventDispatcher<{ dismiss: void }>();
-
-  const saveProduct = (product: Product) => {
-    if (product) {
-      dispatchSave("save", product);
-    }
-  };
-
-  const dismissForm = () => {
-    dispatchDismiss("dismiss");
-  };
-
-  export let product: Product;
-
-  let errors: ZodFormattedError<Product> = {
-    _errors: [],
-  };
-
-  let enableSave = false;
-
-  $: {
-    enableSave = isValidateProduct(product);
+  interface Props {
+    product: Product;
+    onsave?: () => void;
+    ondismiss?: () => void;
   }
 
-  const isValidateProduct = (product: Product): boolean => {
-    const result = ProductSchema.safeParse(product);
-    if (result.success) {
-      errors = {
-        _errors: [],
-      };
-      return true;
-    } else {
-      errors = result.error.format();
-      return false;
-    }
-  };
+  let { product = $bindable(), onsave, ondismiss }: Props = $props();
+
+  let validationResult = $derived(ProductSchema.safeParse(product));
+  let errors = $derived(
+    validationResult.success ? { _errors: [] } : validationResult.error.format(),
+  );
+  let enableSave = $derived(validationResult.success);
 </script>
 
 <form>
@@ -108,12 +82,12 @@
   </div>
   <div class="row justify-content-end modal-footer">
     <div class="col-auto">
-      <button type="button" class="btn btn-secondary" on:click={dismissForm}
+      <button type="button" class="btn btn-secondary" onclick={ondismiss}
         >Close</button
       >
       <button
         class="btn btn-primary"
-        on:click={() => saveProduct(product)}
+        onclick={onsave}
         disabled={!enableSave}>Save Changes</button
       >
     </div>

+ 1 - 1
Products.Frontoffice/svelte.config.js

@@ -1,5 +1,5 @@
 import adapter from '@sveltejs/adapter-auto';
-import { vitePreprocess } from '@sveltejs/kit/vite';
+import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
 
 /** @type {import('@sveltejs/kit').Config} */
 const config = {

+ 80 - 162
README.md

@@ -1,186 +1,104 @@
-# Aplicación de Gestión de Productos con .NET y Svelte
+## Ejemplo de Aplicación .NET con MVC y API REST 🚀
 
-Este proyecto es un ejemplo educativo que muestra cómo crear una aplicación web completa utilizando .NET para el backend y Svelte para el frontend, implementando patrones de diseño modernos y buenas prácticas de desarrollo.
+A continuación, encontrarás instrucciones detalladas sobre cómo configurar y ejecutar este proyecto .NET. El proyecto consta de varios componentes que incluyen lógica de negocio, aplicaciones web y una API REST para administrar productos.
 
-## Descripción del Proyecto
+## Estructura del Proyecto
 
-La aplicación permite gestionar un catálogo de productos con operaciones CRUD (Crear, Leer, Actualizar, Eliminar) a través de diferentes interfaces:
-
-- **API REST**: Ofrece endpoints para manipular productos a través de HTTP
-- **Backoffice (MVC)**: Interfaz administrativa basada en ASP.NET MVC
-- **Frontoffice (Svelte)**: Interfaz de usuario moderna construida con SvelteKit
-
-## Arquitectura del Proyecto
-
-El proyecto sigue una arquitectura en capas con los siguientes componentes:
+La estructura del proyecto es la siguiente:
 
 ```
-- Products.sln                 # Solución principal
-- Products.Common/             # DTOs, excepciones y tipos compartidos
-- Products.Business/           # Lógica de negocio, servicios y repositorios
-- Products.API/                # API REST
-- Products.Backoffice/         # Aplicación MVC para administración
-- Products.Frontoffice/        # Frontend con Svelte
+- global.json
+- Products.sln
+- Products.Common/
+- Products.Business/
+- Products.Backoffice/
+- Products.API/
+- Products.Frontoffice/
 ```
 
-### Patrón de Arquitectura
-
-- **Diseño por Capas**: Separación clara de responsabilidades entre capas
-- **Patrón Repositorio**: Abstracción del acceso a datos
-- **Inyección de Dependencias**: Acoplamiento débil entre componentes
-- **MVC (Model-View-Controller)**: Para la aplicación Backoffice
-- **REST**: Para la API de servicios
-
-## Tecnologías Utilizadas
-
-### Backend
-
-- **.NET 9**: Framework base para el desarrollo
-- **ASP.NET Core**: Para la API REST y aplicación MVC
-- **Entity Framework Core**: ORM para acceso a datos
-- **AutoMapper**: Para mapeo entre objetos de dominio y DTOs
-- **Newtonsoft.Json**: Para serialización/deserialización JSON
-- **In-Memory Database**: Para almacenamiento de datos (en este ejemplo educativo)
-
-### Frontend
-
-- **SvelteKit**: Framework para la creación de la interfaz de usuario
-- **TypeScript**: Para tipado estático en el frontend
-- **Axios**: Para comunicación HTTP con la API
-- **Zod**: Para validación de datos en el cliente
-
-### DevOps
-
-- **Docker**: Para contenerización de la aplicación
-- **Fly.io**: Ejemplo de despliegue en la nube
-
-## Patrones de Diseño Implementados
-
-- **DTO (Data Transfer Objects)**: Para transferencia de datos entre capas
-- **Repositorio**: Para abstracción del acceso a datos
-- **Servicios**: Para encapsular la lógica de negocio
-- **Middleware**: Para manejo centralizado de excepciones
-- **CORS**: Configuración para permitir solicitudes cross-origin
-- **Enumeraciones**: Para tipos de datos con valores predefinidos (Colores)
+- **global.json**: Archivo de configuración global para la versión de .NET SDK.
+- **Products.sln**: Archivo de solución principal del proyecto.
+- **Products.Common**: Contiene DTOs y excepciones compartidas.
+- **Products.Business**: Contiene la lógica de negocio del proyecto, incluidos servicios y repositorios.
+- **Products.Backoffice**: Aplicación MVC para el backoffice de administración.
+- **Products.API**: API REST para administrar productos. En desarrollo, la documentación interactiva está disponible en `/scalar/v1`.
+- **Products.Frontoffice**: Proyecto Svelte que muestra cómo consumir la API REST.
 
-## Configuración del Entorno de Desarrollo
+## Pasos para Iniciar
 
-### Requisitos Previos
+A continuación, se describen los pasos para configurar y ejecutar el proyecto:
 
-- [.NET 9 SDK](https://dotnet.microsoft.com/download/dotnet/9.0)
-- [Node.js](https://nodejs.org/) (v20.x o superior)
+1. **Requisitos Previos**:
+   - Asegúrate de tener instalado el SDK de .NET 10 en tu máquina.
+   - Asegúrate de tener Node.js instalado para ejecutar el proyecto Svelte.
 
-### Pasos para Ejecutar el Proyecto
-
-1. **Clonar el Repositorio**:
-
-   ```
-   git clone <url-del-repositorio>
-   cd Products
-   ```
-
-2. **Restaurar Paquetes NuGet**:
+2. **Clonar el Repositorio**:
+   Clona el repositorio del proyecto desde su ubicación.
 
+3. **Restaurar Paquetes NuGet**:
+   Abre una terminal en la ubicación de la solución (`Products.sln`) y ejecuta el comando:
    ```
    dotnet restore
    ```
 
-3. **Ejecutar la API REST**:
-
-   ```
-   dotnet run --project Products.API
-   ```
-
-   La API estará disponible en: https://localhost:7234 (o http://localhost:5192)
-
-4. **Ejecutar la Aplicación Backoffice (MVC)**:
-
-   ```
-   dotnet run --project Products.Backoffice
-   ```
-
-   La aplicación MVC estará disponible en: https://localhost:7103
-
-5. **Ejecutar la Aplicación Frontoffice (Svelte)**:
-   ```
-   cd Products.Frontoffice
-   npm install
-   npm run dev
-   ```
-   La aplicación Svelte estará disponible en: http://localhost:5173
-
-## Estructura del Código
-
-### Products.Common
-
-Contiene elementos compartidos:
-
-- **Dtos/**: Objetos de transferencia de datos
-- **Types/**: Enumeraciones y tipos personalizados
-- **Exceptions/**: Excepciones personalizadas
-
-### Products.Business
-
-Contiene la lógica de negocio:
-
-- **Domain/**: Entidades de dominio
-- **Service/**: Servicios que implementan la lógica de negocio
-- **Repository/**: Acceso a datos
-- **Persistence/**: Contexto de Entity Framework
-- **AutoMapperProfiles/**: Configuración de mapeo entre entidades y DTOs
-
-### Products.API
-
-API REST:
-
-- **Controllers/**: Endpoints de la API
-- **Middlewares/**: Configuración de middleware para manejo de excepciones
-
-### Products.Backoffice
-
-Aplicación MVC:
-
-- **Controllers/**: Controladores MVC
-- **Views/**: Vistas Razor
-- **Models/**: ViewModels específicos para la UI
-
-### Products.Frontoffice
-
-Aplicación Svelte:
-
-- **src/routes/**: Páginas y componentes
-- **src/lib/**: Utilidades, stores y lógica compartida
-- **src/lib/api.ts**: Cliente de la API REST
-
-## Aspectos Educativos del Proyecto
-
-Este proyecto sirve como referencia para aprender sobre:
-
-1. **Arquitectura en Capas**: Separación de responsabilidades
-2. **API REST**: Principios y buenas prácticas
-3. **Inyección de Dependencias**: Configuración y uso en .NET
-4. **Entity Framework Core**: Configuración básica y seeds
-5. **Manejo de Excepciones**: Middleware centralizado
-6. **Validación de Datos**: Tanto en backend como frontend
-7. **Frontend Moderno**: Integración con frameworks modernos (Svelte)
-8. **Serialización/Deserialización**: Conversión entre objetos y JSON
-9. **Enumeraciones**: Uso en C# y TypeScript
-10. **Patrones de Repositorio y Servicio**: Implementación práctica
-
-## Despliegue
+4. **Ejecutar Aplicaciones .NET**:
+   - Para el proyecto Backoffice, navega a la carpeta `Products.Backoffice` en la terminal y ejecuta:
+     ```bash
+     dotnet run
+     ```
+     o desde la raiz del proyecto:
+     ```bash
+     dotnet run --project Products.Backoffice
+     ```
+   - Para el proyecto API, navega a la carpeta `Products.API` en la terminal y ejecuta:
+     ```bash
+     dotnet run
+     ```
+     o desde la raiz del proyecto:
+     ```bash
+     dotnet run --project Products.API
+     ```
+
+5. **Ejecutar Proyecto Svelte**:
+   - Navega a la carpeta `Products.Frontoffice` en la terminal.
+   - Instala las dependencias ejecutando `npm install`.
+   - Luego, inicia la aplicación con `npm run dev`.
+
+6. **¡Listo!**:
+   Ahora puedes acceder a las aplicaciones desde tu navegador y probar la API REST.
+   Además, el proyecto Svelte te mostrará cómo consumir la API desde un frontend.
+
+## Migraciones (branch `migrations`)
+
+Instalar `dotnet ef` ([how-to](https://learn.microsoft.com/en-us/ef/core/cli/dotnet)) para poder gestionar las migraciones.
+
+- `dotnet ef database update`: Actualiza la base de datos
+- `dotnet ef migrations add <nombre de la migración>`: Crea una nueva migración
+- `dotnet ef migrations remove <nombre de la migración>`: Elimina una migración
+- `dotnet ef migrations list`: Lista las migraciones
+
+```bash
+dotnet ef database update
+dotnet ef migrations add <nombre de la migración>
+dotnet ef migrations remove <nombre de la migración>
+dotnet ef migrations list
+```
 
-El proyecto incluye un Dockerfile para contenerización y puede ser desplegado en servicios como Fly.io:
+**Nota**: Como este proyecto utiliza la base de datos SQLite, es importante que se respete la ubicación de la base de datos.
+Es por ello que los comandos deben ser ejecutados desde la raiz del proyecto haciendo uso del flag `--project`.
 
-```
-fly deploy
+```bash
+dotnet ef database update --project Products.Business
+dotnet ef migrations add <nombre de la migración> --project Products.Business
+dotnet ef migrations remove <nombre de la migración> --project Products.Business
+dotnet ef migrations list --project Products.Business
 ```
 
-Un ejemplo de despliegue está disponible en: https://tsi-products.fly.dev/
+## Deploy en Fly.io
 
-## Licencia
+Se ha realizado un deploy de la aplicación MVC en Fly.io. Puedes acceder a ella desde el siguiente enlace:
+~~https://tsi-products.fly.dev/~~
 
-Este proyecto está bajo la Licencia MIT. Ver el archivo [LICENSE](LICENSE) para más detalles.
+https://products.mz.uy/
 
----
 
-**Nota**: Este proyecto es principalmente educativo y está diseñado para demostrar conceptos y patrones en un entorno simplificado. Algunas prácticas pueden necesitar ser adaptadas para aplicaciones de producción a gran escala.

+ 1 - 1
global.json

@@ -1,6 +1,6 @@
 {
   "sdk": {
-    "version": "9.0.202",
+    "version": "10.0.0",
     "rollForward": "latestMajor",
     "allowPrerelease": true
   }

+ 1 - 1
mise.toml

@@ -1,2 +1,2 @@
 [tools]
-dotnet = "9.0"
+dotnet = "10.0.100"

Some files were not shown because too many files changed in this diff