sábado, 13 de diciembre de 2025

Equivalencias entre un proyecto Spring Boot y ASP .NET Core

En esta ocasión veremos una equivalencia entre un proyecto desarrollado con Spring Boot y ASP .NET Core.

Digamos que tenemos esta estructura de carpetas:

auditor/
  pom.xml
  src/resources/application.properties
  src/main/java/com/inforhomex/CursoSpring/AuditorSpringApplication.java
  src/main/java/com/inforhomex/CursoSpring/component/AuditorComponent.java
  src/main/java/com/inforhomex/CursoSpring/config/AuditorConfig.java
  src/main/java/com/inforhomex/CursoSpring/controller/AuditorController.java
  src/main/java/com/inforhomex/CursoSpring/model/Auditor.java
  src/main/java/com/inforhomex/CursoSpring/services/AuditorService.java
  src/main/java/com/inforhomex/CursoSpring/services/AuditorServiceImpl.java
  src/main/java/com/inforhomex/CursoSpring/repository/AuditorRepository.java

Su equivalente en ASP .NET Core sería:

Auditor/
  Auditor.csproj
  appsettings.json
  Program.cs
  Controllers/
      AuditorController.cs
  Models/
      Auditor.cs
  Services/
      IAuditorService.cs
      AuditorService.cs
  Repositories/
      IAuditorRepository.cs
      AuditorRepository.cs
  Data/
      ApplicationDbContext.cs
  Configuration/
      AuditorConfig.cs (opcional)

Explicación archivo por archivo

Spring usa Maven; .NET usa un archivo XML similar.

Auditor.csproj (equivale al pom.xml)

<Project Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.0" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.0" />
  </ItemGroup>
</Project>

El appsettings.json de ASP .NET Core sería el application.properties de Spring Boot.

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=localhost;Database=AuditorDb;Trusted_Connection=True;"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information"
    }
  }
}

Ahora veamos el programa principal llamado Program.cs que equivaldría a AuditorSpringApplication.java de Spring Boot.

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddDbContext<ApplicationDbContext>();
builder.Services.AddScoped<IAuditorService, AuditorService>();
builder.Services.AddScoped<IAuditorRepository, AuditorRepository>();

var app = builder.Build();

app.MapControllers();
app.Run();

Quien ha usado Spring Boot sabrá lo que es la Inyección de dependencia.

@Autowired
AuditorService service;

En C# también existe ese concepto:

builder.Services.AddSingleton<AuditorComponent>();

¿Y qué con las configuraciones? Con Spring Boot tenemos clases para configuraciones especiales.

@Configuration
public class AuditorConfig { ... }

En ASP .NET Core tenemos esto:

public static class AuditorConfig {
    public static void AddAuditorServices(this IServiceCollection services) {
        services.AddScoped<IAuditorService, AuditorService>();
    }
}

Y en el programa principal Program.cs:

builder.Services.AddAuditorServices();

Las clases Controllers de Spring Boot también tendrían su equivalente:

AuditorController.cs

[ApiController]
[Route("auditor")]
public class AuditorController : ControllerBase {
    private readonly IAuditorService _service;

    public AuditorController(IAuditorService service) {
        _service = service;
    }

    [HttpGet]
    public IActionResult GetAll() => Ok(_service.GetAll());
}

Con las entidades Java pasaría lo mismo:

Auditor.java

@Entity
public class Auditor { ... }

Su equivalente sería:

Auditor.cs

public class Auditor {
    public int Id { get; set; }
    public string Nombre { get; set; }
}

Con las clases servicio de Java/SpringBoot también aplica:

public interface AuditorService { ... }

@Service
public class AuditorServiceImpl implements AuditorService { ... }

Con C#:

IAuditorService.cs

public interface IAuditorService {
    IEnumerable<Auditor> GetAll();
}

AuditorService.cs

public class AuditorService : IAuditorService {
    private readonly IAuditorRepository _repo;

    public AuditorService(IAuditorRepository repo) {
        _repo = repo;
    }

    public IEnumerable<Auditor> GetAll() => _repo.GetAll();
}

Y continuamos con las clases Repository:

AuditorRepository.java

@Repository
public interface AuditorRepository extends JpaRepository<Auditor, Long> { }

Y su equivalente:

Interface:

public interface IAuditorRepository {
    IEnumerable<Auditor> GetAll();
}

Implementación:

public class AuditorRepository : IAuditorRepository {
    private readonly ApplicationDbContext _context;

    public AuditorRepository(ApplicationDbContext context) {
        _context = context;
    }

    public IEnumerable<Auditor> GetAll() => _context.Auditors.ToList();
}

ApplicationDbContext.cs

public class ApplicationDbContext : DbContext {
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options) { }

    public DbSet<Auditor> Auditors { get; set; }
}

Con esto tendríamos la estructura de directorios equivalente a Spring Boot con ASP .NET Core :

- Controllers
- Models
- Services
- Repositories
- DbContext
- Configuración en Program.cs
- appsettings.json

Esta estructura no necesariamente la tendría un proyecto real. Sin embargo, es un acercamiento bastante formal.

Ahora veamos una tabla comparativa.

Comparativa ASP .NET Core vs Spring Boot


Criterio ASP.NET Core (.NET) Spring Boot (Java)
Lenguaje principal C#, F#, VB.NET Java, Kotlin, otros JVM
Plataformas soportadas Windows, Linux, macOS (cross-platform) Windows, Linux, macOS (via JVM)
Modelo de ejecución .NET runtime (Core/5+) con JIT y opciones AOT JVM (HotSpot, OpenJ9) con JIT
Tipo de framework Framework web moderno, modular, orientado a middleware Framework de arranque rápido sobre Spring, opinionated
Arquitectura y patrón principal Soporta MVC, Razor Pages, Minimal APIs, gRPC, SignalR Soporta MVC, WebFlux (reactivo), REST, SOAP, gRPC (via librerías)
Configuración appsettings.json, configuración fuertemente tipada application.properties / application.yml, Profiles
Inyección de dependencias Contenedor DI integrado en el framework Spring IoC Container muy maduro y extensible
Rendimiento en APIs Muy alto; entre los más rápidos en benchmarks Alto, generalmente por debajo de ASP.NET Core en pruebas crudas
Servidor embebido Kestrel (servidor web propio, muy eficiente) Tomcat por defecto; Jetty/Undertow opcionales
ORM / acceso a datos Entity Framework Core, Dapper, otros ORMs Spring Data JPA, Hibernate, MyBatis, etc.
Soporte cloud Excelente integración con Azure; buen soporte AWS/GCP Excelente integración con AWS, GCP, Azure (Spring Cloud)
Ecosistema y librerías Ecosistema .NET, NuGet, fuerte integración con herramientas Microsoft Ecosistema Java, gran cantidad de librerías, Spring Cloud, Spring Security, etc.
Curva de aprendizaje Amigable si vienes de C# o stack Microsoft Puede ser más compleja por la profundidad del ecosistema Spring
Herramientas y IDEs Visual Studio, Rider, VS Code IntelliJ IDEA, Eclipse, VS Code
Madurez en entornos enterprise Muy usado en enterprise, fuerte en empresas Microsoft-centric Dominante histórico en enterprise Java, ecosistema muy maduro
Licencia y open source Open source (MIT), desarrollado por Microsoft y comunidad Open source (Apache 2.0), desarrollado por VMware/Spring y comunidad
Casos de uso típicos APIs de alto rendimiento, microservicios, backends corporativos, integraciones con Azure Microservicios enterprise, sistemas bancarios, telecom, SaaS sobre JVM

Conclusiones:

ASP.NET Core y Spring Boot representan dos de los ecosistemas más sólidos, modernos y maduros para construir aplicaciones backend y microservicios. Ambos ofrecen rendimiento, modularidad, herramientas de desarrollo avanzadas y una comunidad activa, pero cada uno destaca en áreas distintas.

ASP.NET Core sobresale por su alto rendimiento, su servidor web Kestrel extremadamente eficiente y su integración natural con el ecosistema Microsoft, especialmente Azure. Su diseño minimalista, su DI nativo y su enfoque en middleware lo hacen muy directo y consistente.

Para equipos que ya trabajan con C#, Windows o Azure, ASP.NET Core suele ser la opción más natural y productiva.

Spring Boot, por otro lado, brilla por la amplitud y madurez de su ecosistema, especialmente en entornos enterprise donde Java ha sido dominante durante décadas.

Su integración con Spring Cloud, su flexibilidad para trabajar con múltiples servidores embebidos y su enorme catálogo de librerías lo convierten en una plataforma muy poderosa para arquitecturas complejas y distribuidas.

En términos prácticos, ninguno es “mejor” de forma absoluta. La elección depende del contexto:

  • Si buscas máximo rendimiento, integración con Azure, o ya trabajas con C#, ASP.NET Core es una apuesta excelente. 
  • Si necesitas ecosistema enterprise, herramientas maduras para microservicios distribuidos o ya tienes experiencia en Java, Spring Boot sigue siendo un estándar de oro.

En resumen, ambos frameworks son tecnologías de primera línea. La decisión final suele depender más del ecosistema, la experiencia del equipo y las necesidades del proyecto que de diferencias técnicas absolutas.

Enlaces:

https://blog.vtutor.com/es/cursos-en-linea/principales-razones-por-las-que-asp-net-core-es-el-mejor-framework-para-el-desarrollo-de-aplicaciones-web/
https://www.swiftorial.com/matchups/backend_framework/aspnet-core-vs-spring-boot-dotnet-vs-java
https://medium.com/@solomongetachew112/spring-boot-vs-net-core-which-backend-framework-wins-in-2024-2025-de9395008244
https://dev.to/umesh_kushwaha_6655ba4c0d/spring-boot-vs-net-core-complete-developer-migration-guide-4mfk

viernes, 12 de diciembre de 2025

Lo nuevo de C# 14

 

En está ocasión veremos las nuevas características del lenguaje de programación C#:

  • Miembros de extensión (extension members). 
  • Asignación nula condicional. 
  • Palabra clave field.
  •  nameof para genéricos no enlazados. 
  • Constructores y eventos parciales. 
  • Conversiones implícitas para Span<T>
  • Modificadores en Parámetros Lambda.

1. Miembros de extensión (extension members)

Con los  extension members podemos extender interfaces con miembros adicionales, incluyendo propiedades y métodos, sin modificar la interfaz original. Esto abre la puerta a patrones más limpios, especialmente en librerías y arquitectura modular.

Los extension members permiten:

  • Añadir implementaciones por defecto a interfaces sin romper compatibilidad. 
  • Crear APIs más expresivas sin modificar código fuente de terceros. 
  •  Encapsular comportamientos comunes en un solo lugar. 
  •  Reducir boilerplate en implementaciones repetitivas. 
  •  Evolucionar interfaces sin romper implementaciones existentes.

Ejemplo 1. Extender una interfaz con un método.

Teniendo está interface (que no podemos modificar) y agregar un nuevo método para el cálculo del perímetro.

public interface IShape
{
    double Area();
}

Podemos agregarlo de esta manera sin tocar la interface:

public interface IShape
{
    double Area();
}

public static class ShapeExtensions
{
    public static double Perimeter(this IShape shape)
    {
        return Math.Sqrt(shape.Area() / Math.PI) * 2;

    }
}

Ejemplo 2. Extender una interfaz con una propiedad, lo que antes era imposible.

public interface IShape
{
    double Area();
}

public static class ShapeExtensions
{
    public static double Diameter(this IShape shape)
    {
        return Math.Sqrt(shape.Area() / Math.PI) * 2;
    }

    public static double Radius(this IShape shape)
        => Diameter(shape) / 2;
}

Y ahora:

public static class ShapeExtensions
{
    public static double Radius(this IShape shape)
        => Math.Sqrt(shape.Area() / Math.PI);
}

Podemos usarlo de esta forma:

IShape circle = new Circle(10);
Console.WriteLine(circle.Radius());

2. Asignación nula condicional

Básicamente es una forma de decir: solo asigna este valor si la variable no es null. Su sintaxis es:

x?.atributo = valor;

Ejemplo de uso:

Usuario? usuario = ObtenerUsuario();

usuario?.Edad = 30; // Solo si usuario no es null

En lugar de hacer esto:

if(usuario != null){
  usuario.Edad = 30;
}

Esto nos ayuda con:

  • Menos if repetitivos. 
  • Código más expresivo y seguro. 
  • Menos ruido en handlers, mappers y servicios. 
  • Mejor legibilidad en pipelines donde muchos objetos pueden ser null.


3. Palabra clave field

field es una palabra clave contextual que te permite acceder al campo de respaldo (backing field) que el compilador genera automáticamente para una propiedad. Es decir, te deja usar propiedades automáticas pero con lógica personalizada, sin tener que declarar un campo privado manualmente.

Sirve para:

  • Agregar validaciones en el setter sin crear un campo privado. 
  • Agregar normalización (trim, toUpper, etc.). 
  • Agregar restricciones (rangos, valores mínimos). 
  • Mantener el código limpio y compacto. 
  • Evitar el boilerplate clásico:
private int _edad;
public int Edad
{
    get => _edad;
    set => _edad = value < 0 ? 0 : value;
}

Con field , ya no necesitas ese campo _edad :

public int Edad
{
    get;
    set => field = value < 0 ? 0 : value;
}

field nos ayuda a:

  • No depender de campos extra o campos privados. 
  • Podemos hacer uso de propiedades compactas. 
  • Tener menos código repetitivo. 
  • Tener un código más legible y expresivo.

4. nameof para genéricos no enlazados

Según la documentación oficial: nameof ahora soporta tipos genéricos no enlazados (unbound generic types).

Ejemplo 1. Especificar un tipo concreto.

Antes de C# 14:

nameof(List<int>)  // "List"

Pero no podías hacer esto:

nameof(List<>)   // Error antes de C# 14

Con C# 14 si se pude:

Console.WriteLine(nameof(Dictionary<,>));
// Resultado: "Dictionary"

Console.WriteLine(nameof(List<>));
// Resultado: "List"

5. Constructores y eventos parciales

Con esta nueva carcaterística: puedes declarar constructores como partial, igual que métodos o propiedades.

Esto es útil porque:

  • Puedes dividir un constructor entre varios archivos de una clase parcial. 
  • Una parte puede declarar la firma. 
  • Otra parte puede contener la implementación.

Ejemplo. Veamos un uso de constructor parcial.

Generado automáticamente:

public partial class Persona
{
    public partial Persona(string nombre);
}

Tu implementación:

public partial class Persona
{
    public partial Persona(string nombre)
    {
        Nombre = nombre;
    }

    public string Nombre { get; }
}

Usar esta nueva característica nos sirve para:

  • Separar lógica generada de lógica manual. 
  • Evitar sobrescribir código cuando regeneras archivos. 
  • Mantener clases grandes organizadas. 
  • Permitir que generadores agreguen inicialización sin romper tu constructor.

¿Y qué con los eventos parciales? Pues, ahora puedes declarar eventos como partial.

Esto permite dividir:

  • La declaración del evento. 
  • La implementación del add/remove.

Igual que con los constructores, esto es ideal para escenarios donde parte del código es generado.

¿Para qué sirve?

  • Separar la lógica de suscripción generada de la lógica manual. 
  • Permitir que generadores agreguen eventos sin interferir con tu código. 
  • Mantener clases grandes más limpias.

6. Conversiones implícitas para Span<T>

Con C# 14 se agregan más conversiones implícitas entre Span<T>, ReadOnlySpan<T> y (T[]).

¿Para qué sirven?

Estas conversiones implícitas permiten:

  • Usar Span<T> y ReadOnlySpan<T> de forma más natural. 
  • Evitar copias innecesarias de memoria. 
  •  Escribir código más rápido y eficiente sin complicaciones. 
  •  Simplificar APIs que trabajan con buffers o slices de memoria. 
  •  Reducir la fricción al pasar arreglos a métodos que esperan spans.

En pocas palabras:

Hacen que Span<T> sea un ciudadano de primera clase en el lenguaje.

Ejemplo. Si tenías un método que recibía un Span<int> :

void Procesar(Span<int> datos)
{
    // ...
}

Y querías pasarle un arreglo, necesitabas conversión explícita:

int[] numeros = { 1, 2, 3, 4 };

// Antes: necesitabas conversión explícita
Procesar(numeros.AsSpan());

Ahora con C# 14 lo haces directamente:

int[] numeros = { 1, 2, 3, 4 };

// Conversión implícita
Procesar(numeros);

Resumen:

Conversión Antes de C# 14 Con C# 14 Ejemplo
T[] → Span<T> No implícita Implícita Procesar(numeros);
T[] → ReadOnlySpan<T> No implícita Implícita Leer(numeros);
Span<T> → ReadOnlySpan<T> No implícita Implícita ReadOnlySpan<byte> r = buffer;
string → ReadOnlySpan<char> Ya existía Mejorada Imprimir("Hola");

7. Modificadores en Parámetros Lambda

Esta nueva característica nos permite usar modificadores en los parámetros de una expresión lambda, igual que en un método normal.

Los modificadores permitidos incluyen:

  • ref 
  • in 
  • out 
  • params

Esto significa que puedes escribir lambdas que:

  • Pasan argumentos por referencia. 
  • Evitan copias innecesarias. 
  • Trabajan con parámetros de solo lectura. 
  • Aceptan un número variable de argumentos.

Con esto, las lambdas ahora pueden comportarse como métodos completos.

¿Para qué sirven?

  • Para mejorar el rendimiento (evitar copias grandes). 
  • Para permitir APIs más flexibles. 
  • Para trabajar con estructuras (struct) sin overhead
  • Para escribir código más expresivo y seguro. 
  • Para usar lambdas en escenarios donde antes solo podías usar métodos normales.

Ejemplo 1. Uso de lambda con in (solo lectura). Supongamos que tienes una estructura grande:

public struct Punto
{
    public int X;
    public int Y;
}

Antes, si querías procesarla en una lambda, se copiaba completa. Ahora puedes usar in:

Action<in Punto> imprimir = (in Punto p) =>
{
    Console.WriteLine($"({p.X}, {p.Y})");
};

Uso:

var punto = new Punto { X = 10, Y = 20 };
imprimir(in punto);

Con esto:

  • No se copia la estructura. 
  • Solo de lectura. 
  • Es más eficiente.

Ejemplo 2. Uso de lambda con ref.

var numero = 5;

Action<ref int> duplicar = (ref int n) =>
{
    n *= 2;
};

duplicar(ref numero);

Console.WriteLine(numero); // 10

Con esto: La lambda modifica la variable original. Igual que un método con ref.

Resumen:

Modificador ¿Qué hace? ¿Para qué sirve? Ejemplo
in Pasa el parámetro por referencia de solo lectura Evita copias de estructuras grandes (in Punto p) => Console.WriteLine(p.X);
ref Pasa el parámetro por referencia modificable Permite cambiar el valor original (ref int n) => n *= 2;
out Parámetro de salida Devolver múltiples valores desde una lambda (out int r) => r = 10;
params Acepta un número variable de argumentos Más flexibilidad en lambdas (params int[] v) => v.Sum();

C# es un lenguaje de programación muy completo que puede ser usado en muchos ámbitos. Se puede emplear casi cualquier paradigma de programación existente como la POO y la Programación funcional. Sus nuevas características lo extienden aún más y proveen al desarrollador más herramientas para hacer de su código algo más eficiente y de calidad.

Resumiendo todo lo anteriormente visto tenemos una tabla resumen de lo anteriormente visto:

Característica ¿Qué es? ¿Para qué sirve? Ejemplo breve
Miembros de extensión
(extension members)
Permiten extender interfaces con métodos, propiedades y más. Agregar funcionalidad sin modificar el tipo original. IShape s; s.Perimeter();
Asignación nula condicional Permite asignar solo si el objeto no es null. Evita if (x != null) repetitivos. persona?.Nombre = "Juan";
Palabra clave field Accede al campo de respaldo de una propiedad automática. Permite validaciones sin declarar campos privados. set => field = value.Trim();
nameof para genéricos no enlazados Permite usar nameof con tipos genéricos abiertos. Mejora logging, diagnósticos y metaprogramación. nameof(Dictionary<,>)
Constructores y eventos parciales Permiten dividir su declaración e implementación. Separar código generado del código manual. public partial Persona(...)
Conversiones implícitas para Span<T> Permiten convertir arreglos y spans sin código explícito. Mejor rendimiento y APIs más limpias. Procesar(numeros);
Modificadores en parámetros lambda Permiten usar ref, in, out, params en lambdas. Más control, eficiencia y expresividad. (ref int n) => n++;

Seguiremos con esta serie sobre C#.

Enlaces:

https://learn.microsoft.com/es-es/dotnet/csharp/whats-new/csharp-14


lunes, 8 de diciembre de 2025

Criptomonedas explicadas de manera sencilla (o casi)

En una entrega anterior dimos un vistazo a lo que son las criptomonedas. Las cual es:

  • Una entidad NO FÍSICA  basada en blockchain que solo existe en una red distribuida.
  • Una entidad NO FÍSICA descentralizada, no depende de una entidad gubernamental o bancaria.
  • Una entidad NO FÍSICA segura puesto que utiliza criptografía para realizar sus transacciones, y así evitar copias o fraudes (en teoría). 
  • Una entidad NO FÍSICA cuyo valor es ambiguo o volátil, según la oferta o la demanda (ojo ahí). 
  • Una entidad NO FÍSICA que se almacena en "carteras digitales" llamada Wallets.

Si desglosamos etimológicamente la palabra CRIPTOMONEDA nos encontramos con dos vocablos:

  • Cripto: cubierto, escondido. 
  • Monete o monere: recordar, advertir, aconsejar.

¿Cuál es el sentido, objetivo o fin de las criptomonedas?

Las criptomonedas nacen con un propósito central: crear un sistema de dinero digital que funcione sin depender de bancos, gobiernos o intermediarios, permitiendo transacciones seguras, globales y transparentes.

  • Las criptomonedas buscan ser un sistema de pagos seguro basado en criptografía, sin necesidad de bancos centrales.
  • Su objetivo es ofrecer un medio descentralizado y digital para realizar transacciones financieras, reduciendo la dependencia de instituciones tradicionales. 
  • También responden a la necesidad de finanzas descentralizadas (DeFi) y a la creación de nuevos casos de uso en la economía digital.

Esto se basa en la tecnología blockchain, que garantiza seguridad y descentralización.

Las criptomonedas existen para crear un sistema financiero:

  • Descentralizado.
  • Seguro.
  • Global.
  • Transparente.
  • Accesible para todos. 
  • Innovador y programable.

No son solo dinero digital: son la base de una nueva infraestructura económica.

Las criptomonedas más conocidas son:

  • Bitcoin (BTC).
  • Ethereum (ETH).
  • Litecoin (LTC).

¿Para qué sirve?

  • Para comprar bienes y servicios (aunque no en todos lados). 
  • Para invertir o especular con su precio. 
  • Para transferir dinero de forma rápida y global sin intermediarios.

La tecnología que hace posible a Bitcoin, Ethereum y muchas otras criptomonedas se le conoce como blockchain (cadena de bloques).

La blockchain es, esencialmente, un libro de registros digital distribuido donde se guardan transacciones de forma segura, transparente e inmutable.

1. Estructura: bloques encadenados

Cada bloque contiene:

  • Datos de transacciones.
  • Un hash (identificador único).
  • El hash del bloque anterior.

Esto crea una cadena donde si cambias un bloque, rompes toda la cadena, lo que evita manipulaciones.

2. Red distribuida

Miles de computadoras (nodos) guardan una copia completa de la cadena. Esto elimina la necesidad de un intermediario central y aumenta la seguridad.

3. Mecanismos de consenso

Para validar transacciones, la red usa algoritmos como:

  • Proof of Work (PoW) – usado por Bitcoin.
  • Proof of Stake (PoS) – usado por Ethereum 2.0.

Estos mecanismos permiten que todos los nodos se pongan de acuerdo sin confiar unos en otros.

4. Inmutabilidad y seguridad

Una vez que un bloque se registra, no puede modificarse sin rehacer toda la cadena, lo cual es computacionalmente inviable.

Todo esto suena bonito y agradable, pero ¿Cómo se compran? Veamos un paso a paso.

1. Elige un exchange (plataforma)

Según las guías actuales, en México se usan plataformas como:

  • Binance. 
  • Bitso. 
  • Coinbase. 
  • KuCoin.

Estas plataformas permiten comprar, vender y almacenar criptomonedas.

2. Crea una cuenta y verifica tu identidad

Los exchanges regulados te pedirán:

  • Identificación oficial. 
  • Selfie. 
  • Comprobante básico de datos.

Esto es parte del proceso KYC (Know Your Customer).

3. Deposita fondos

Puedes depositar:

  • Transferencia bancaria SPEI. 
  • Tarjeta de débito. 
  • A veces tarjeta de crédito (con comisiones más altas).

4. Compra la criptomoneda

En la plataforma eliges:

  • La moneda (BTC, ETH, USDT, etc.). 
  • El monto.
  • El tipo de orden (mercado o límite).

5. Guarda tus criptomonedas

Opciones:

  • Wallet del exchange (más fácil, menos segura). 
  • Wallet propia (Metamask, Ledger, Trezor). 
  • Cold wallets para máxima seguridad.

Proceso de transacción de una bitcoin

Veamos un diagrama que describe el flujo de una transacción de bitcoin.

                   ┌──────────────────────────┐
1. Usuario crea transacción│
- Monto                  │
- Dirección destino      │
                   └─────────────┬────────────┘


                   ┌──────────────────────────┐
2. Wallet selecciona UTXO │
                   │  (entradas disponibles)   │
                   └─────────────┬────────────┘


                   ┌──────────────────────────┐
3. Wallet construye       │
                   │    la transacción         │
- Inputs (UTXO)          │
- Outputs (destino y     │
                   │    cambio)                │
                   └─────────────┬────────────┘


                   ┌──────────────────────────┐
4. Firma digital con      │
                   │    clave privada          │
                   └─────────────┬────────────┘


                   ┌──────────────────────────┐
5. Se envía a un nodo     │
                   │    de la red Bitcoin      │
                   └─────────────┬────────────┘


                   ┌──────────────────────────┐
6. Nodo valida reglas     │
- Firma válida           │
- UTXO no gastado        │
- Suma entradas ≥ salidas│
                   └─────────────┬────────────┘


                   ┌──────────────────────────┐
7. Transacción entra al   │
                   │    MEMPOOL (pendiente)    │
                   └─────────────┬────────────┘


                   ┌──────────────────────────┐
8. Mineros seleccionan    │
                   │    transacciones según fee│
                   └─────────────┬────────────┘


                   ┌──────────────────────────┐
9. Minero arma un bloque  │
                   │    y ejecuta PoW          │
                   └─────────────┬────────────┘


                   ┌──────────────────────────┐
10. Bloque válido se       │
                   │    difunde a la red       │
                   └─────────────┬────────────┘


                   ┌──────────────────────────┐
11. Nodos verifican bloque │
                   │    y lo agregan a la      │
                   │    blockchain             │
                   └─────────────┬────────────┘


                   ┌──────────────────────────┐
12. Transacción CONFIRMADA │
                   │    (1 confirmación)       │
                   └─────────────┬────────────┘


                   ┌──────────────────────────┐
13. Más bloques encima     │
                   │    = más seguridad        │
                   │    (6 confirmaciones)     │
                   └──────────────────────────┘

Este diagrama es solo un ejemplo de transacción.

Conclusión:

El mundo de las criptomonedas es aún lugar recóndito y no muy fácil de navegar (quien diga lo contrario miente). Encontrarás a cada embustero y vende-humos que dirá lo fácil que es usar para comprar e invertir, aunque él no tiene ni idea de cómo hacerlo. Millonarios que heredaron su fortuna que promueven criptomonedas que jamás han usado, cursos sobre mentes millonarias (con un desembolso monetario por supuesto) y cómo lograrlo en sencillos pasos .

A las personas de a pie solo nos queda informarnos y no dejarnos llevar por la ola que arrastra a miles de incautos. No deberíamos creer en todas las maravillas de está tecnología, siempre hay riesgos y casi nadie habla de ello. Siempre hay algo oculto cuando sale algo "innovador".

Como desarrolladores, deberíamos conocer cual es la pauta a seguir. Que plataforma(s) y lenguaje(s) son los que deberíamos aprender.

Enlaces:

https://ethereum.org/en/developers/
https://docs.soliditylang.org/en/latest/
https://codemonkeyjunior.blogspot.com/2025/03/criptomonedas-que-son-y-para-que-sirven.html
https://alquimistadecodigo.blogspot.com/2025/12/comparativa-rholang-vs-plutus-vs.html


viernes, 5 de diciembre de 2025

LINQ: consultando documentos XML

Como vimos en la entrega anterior LINQ nos permite realizar consultas de una base de datos o archivos XML.

Suponiendo que tenemos un documento XML (empleado.xml) con el siguiente contenido:

<?xml version="1.0" encoding="UTF-8"?>
<Empleados>
<Empleado> 
<Id>4392552</Id>
<Nombre>Horacio</Nombre>
<Apellidos>Gomez Torres</Apellidos>
<Correo>horacio.gomez.tor@infotec.com</Correo>
<Salario>25000.00</Salario>
<Departamento>202</Departamento>
</Empleado>

<Empleado>
<Id>4292856</Id>
<Nombre>Veronica</Nombre>
<Apellidos>Uribe Gomora</Apellidos>
<Correo>veronica.uribe.gom@infotec.com</Correo>
<Salario>30000.00</Salario>
<Departamento>204</Departamento>
</Empleado>


<Empleado>
<Id>4701330</Id>
<Nombre>Karla</Nombre>
<Apellidos>Perez Perez</Apellidos>
<Correo>karla.perez.per@infotec.com</Correo>
<Salario>28000.00</Salario>
<Departamento>200</Departamento>
</Empleado>

</Empleados>

Lo que sigue es la creación de un proyecto .NET que nos permita leer ese documento y hacer consultas:

1. Creamos un nuevo proyecto con dotnet:

$ dotnet new console -n DemoLinqXml
$ cd DemoLinqXml

El documento XML (empleados.xml) debe estar en la misma ruta del proyecto. Ahora abrimos el programa principal Program.cs y lo editamos:

using System;
using System.Linq;
using System.Xml.Linq;

class Program
{
    static void Main()
    {
        XDocument doc = XDocument.Load("empleados.xml");

        var empleados = doc.Root.Elements("Empleado");

        // Aquí vamos a incluir las consultas
    }
}

Podríamos obtener todos los datos (como un SELECT):

using System;
using System.Linq;
using System.Xml.Linq;

class Program
{
    static void Main()
    {

        try
        {
            XDocument doc = XDocument.Load("empleados.xml");
            var empleados = doc.Root.Elements("Empleado");

            if (empleados != null)
            {
                Console.WriteLine("\t Obtener todos los empleados:");
                var todos = empleados.Select(e => new
                {
                    Id = (int)e.Element("Id"),
                    Nombre = (string)e.Element("Nombre"),
                    Apellidos = (string)e.Element("Apellidos"),
                    Correo = (string)e.Element("Correo"),
                    Salario = (decimal)e.Element("Salario"),
                    Departamento = (int)e.Element("Departamento")
                });

                foreach (var emp in todos)
                {
                    Console.WriteLine($"ID: {emp.Id}");
                    Console.WriteLine($"Nombre: {emp.Nombre} {emp.Apellidos}");
                    Console.WriteLine($"Correo: {emp.Correo}");
                    Console.WriteLine($"Salario: {emp.Salario}");
                    Console.WriteLine($"Departamento: {emp.Departamento}");
                    Console.WriteLine("-------------------------");
                }                

            }
        }
        catch (FileNotFoundException ex)
        {
            Console.WriteLine("El archivo no existe: {0}", ex.Message);
        }

    }
}

Si queremos obtener solo nombre y correo:

using System;
using System.Linq;
using System.Xml.Linq;

class Program
{
    static void Main()
    {

        try
        {
            XDocument doc = XDocument.Load("empleados.xml");
            var empleados = doc.Root.Elements("Empleado");

            if (empleados != null)
            {
               
                
                Console.WriteLine("\t Obtener solo nombre y correo:");
                var contactos = empleados
                .Select(e => new
                {
                    Nombre = (string)e.Element("Nombre"),
                    Correo = (string)e.Element("Correo")
                });
                foreach (var contacto in contactos)
                {
                    Console.WriteLine($"{contacto.Nombre} : {contacto.Correo}");
                }

            }
        }
        catch (FileNotFoundException ex)
        {
            Console.WriteLine("El archivo no existe: {0}", ex.Message);
        }

    }
}

Obtener el empleado que gana más:

Console.WriteLine("\t Obtener el empleado que gana mas:");
var top = empleados.OrderByDescending(e => (decimal)e.Element("Salario")).First();
Console.WriteLine($"El mayor salario es: \n{top} ");

Obtener todos los empleados que ganen mas de $26000.00 (usando una sintaxis similar a SQL):

Console.WriteLine("\t Obtener todos los empleados que ganen mas de $26000.00:");
var query = from e in empleados
                            where (decimal)e.Element("Salario") > 26000
                            orderby (string)e.Element("Nombre")
                            select new
                            {
                                Nombre = (string)e.Element("Nombre"),
                                Salario = (decimal)e.Element("Salario")
                            };

foreach (var emp in query)
{
  Console.WriteLine($"{emp.Nombre} - {emp.Salario:C}");                
}

Obtener empleado del departamento 202:

Console.WriteLine("\t Obtener empleado del departamento 202:");
var depto202 = empleados.Where(e => (int)e.Element("Departamento") == 202);

foreach (var emp in depto202)
{
   Console.WriteLine($"ID: {(int)emp.Element("Id")}");
   Console.WriteLine($"Nombre: {(string)emp.Element("Nombre")}");
   Console.WriteLine($"Apellidos: {(string)emp.Element("Apellidos")}");
   Console.WriteLine($"Correo: {(string)emp.Element("Correo")}");
   Console.WriteLine($"Salario: {(decimal)emp.Element("Salario")}");
   Console.WriteLine($"Departamento: {(int)emp.Element("Departamento")}");
   Console.WriteLine("-------------------------");
}

Código completo:

using System;
using System.Linq;
using System.Xml.Linq;

class Program
{
    static void Main()
    {

        try
        {
            XDocument doc = XDocument.Load("empleados.xml");
            var empleados = doc.Root.Elements("Empleado");

            if (empleados != null)
            {
                Console.WriteLine("\t Obtener todos los empleados:");
                var todos = empleados.Select(e => new
                {
                    Id = (int)e.Element("Id"),
                    Nombre = (string)e.Element("Nombre"),
                    Apellidos = (string)e.Element("Apellidos"),
                    Correo = (string)e.Element("Correo"),
                    Salario = (decimal)e.Element("Salario"),
                    Departamento = (int)e.Element("Departamento")
                });

                foreach (var emp in todos)
                {
                    Console.WriteLine($"ID: {emp.Id}");
                    Console.WriteLine($"Nombre: {emp.Nombre} {emp.Apellidos}");
                    Console.WriteLine($"Correo: {emp.Correo}");
                    Console.WriteLine($"Salario: {emp.Salario}");
                    Console.WriteLine($"Departamento: {emp.Departamento}");
                    Console.WriteLine("-------------------------");
                }
                
                Console.WriteLine("\t Obtener solo nombre y correo:");
                var contactos = empleados
                .Select(e => new
                {
                    Nombre = (string)e.Element("Nombre"),
                    Correo = (string)e.Element("Correo")
                });
                foreach (var contacto in contactos)
                {
                    Console.WriteLine($"{contacto.Nombre} : {contacto.Correo}");
                }

                Console.WriteLine("\t Obtener el empleado que gana mas:");
                var top = empleados.OrderByDescending(e => (decimal)e.Element("Salario")).First();
                Console.WriteLine($"El mayor salario es: \n{top} ");

                Console.WriteLine("\t Obtener todos los empleados que ganen mas de $26000.00:");
                var query = from e in empleados
                            where (decimal)e.Element("Salario") > 26000
                            orderby (string)e.Element("Nombre")
                            select new
                            {
                                Nombre = (string)e.Element("Nombre"),
                                Salario = (decimal)e.Element("Salario")
                            };

                foreach (var emp in query)
                {
                    Console.WriteLine($"{emp.Nombre} - {emp.Salario:C}");
                }


                Console.WriteLine("\t Obtener empleado del departamento 202:");
                var depto202 = empleados.Where(e => (int)e.Element("Departamento") == 202);
                foreach (var emp in depto202)
                {
                    Console.WriteLine($"ID: {(int)emp.Element("Id")}");
                    Console.WriteLine($"Nombre: {(string)emp.Element("Nombre")}");
                    Console.WriteLine($"Apellidos: {(string)emp.Element("Apellidos")}");
                    Console.WriteLine($"Correo: {(string)emp.Element("Correo")}");
                    Console.WriteLine($"Salario: {(decimal)emp.Element("Salario")}");
                    Console.WriteLine($"Departamento: {(int)emp.Element("Departamento")}");
                    Console.WriteLine("-------------------------");
                }

            }
        }
        catch (FileNotFoundException ex)
        {
            Console.WriteLine("El archivo no existe: {0}", ex.Message);
        }

    }
}

Ejecutamos el código:

$ dotnet run

Salida:

         Obtener todos los empleados:
ID: 4392552
Nombre: Horacio Gomez Torres
Correo: horacio.gomez.tor@infotec.com
Salario: 25000.00
Departamento: 202
-------------------------
ID: 4292856
Nombre: Veronica Uribe Gomora
Correo: veronica.uribe.gom@infotec.com
Salario: 30000.00
Departamento: 204
-------------------------
ID: 4701330
Nombre: Karla Perez Perez
Correo: karla.perez.per@infotec.com
Salario: 28000.00
Departamento: 200
-------------------------
         Obtener solo nombre y correo:
Horacio : horacio.gomez.tor@infotec.com
Veronica : veronica.uribe.gom@infotec.com
Karla : karla.perez.per@infotec.com
         Obtener el empleado que gana mas:
El mayor salario es:
<Empleado>
  <Id>4292856</Id>
  <Nombre>Veronica</Nombre>
  <Apellidos>Uribe Gomora</Apellidos>
  <Correo>veronica.uribe.gom@infotec.com</Correo>
  <Salario>30000.00</Salario>
  <Departamento>204</Departamento>
</Empleado>
         Obtener todos los empleados que ganen mas de $26000.00:
Karla - $28,000.00
Veronica - $30,000.00
         Obtener empleado del departamento 202:
ID: 4392552
Nombre: Horacio
Apellidos: Gomez Torres
Correo: horacio.gomez.tor@infotec.com
Salario: 25000.00
Departamento: 202
-------------------------

Ventajas y desventajas de usar LINQ for XML

Aquí una pequeña tabla con las ventajas y desventajas del uso de LINQ para leer documentos XML.

Aspecto Ventajas Desventajas
Rendimiento Rápido para XML pequeños o medianos No apto para XML muy grandes (carga completa en memoria)
Legibilidad Sintaxis clara, declarativa y fácil de mantener Puede ser más verboso que XPath en consultas profundas
Tipado Conversión automática a tipos C# Puede lanzar excepciones si faltan nodos o tipos incorrectos
Flexibilidad Fácil de combinar con otras colecciones LINQ Menos eficiente para búsquedas complejas o profundas
Portabilidad Integrado y natural en .NET No es estándar fuera del ecosistema .NET

Seguiremos con esta serie sobre tecnología .NET y C#.

Enlaces:

https://codemonkeyjunior.blogspot.com/2025/11/linq-un-lenguaje-de-consulta-para-c.html
https://learn.microsoft.com/es-es/dotnet/standard/linq/linq-xml-overview

Equivalencias entre un proyecto Spring Boot y ASP .NET Core

En esta ocasión veremos una equivalencia entre un proyecto desarrollado con Spring Boot y ASP .NET Core. Digamos que tenemos esta estru...

Etiquetas

Archivo del blog