3 puntos por GN⁺ 2024-10-23 | 1 comentarios | Compartir por WhatsApp
  • Rwf es un framework integral para crear aplicaciones web con Rust, basado en el patrón clásico MVC, y ofrece por defecto las funciones necesarias para construir apps web rápidas y seguras
  • Entre sus funciones básicas se incluyen un servidor HTTP, un ORM para consultas PostgreSQL, plantillas dinámicas, autenticación y sesiones de usuario, middleware, trabajos en segundo plano y programados, y migraciones de base de datos
  • También ofrece un framework REST integrado con soporte para serialización JSON, WebSockets, hosting de archivos estáticos, configuración por entorno, logging, métricas y CLI
  • Está estrechamente integrado con Hotwired Turbo, lo que permite crear SPA impulsadas desde el backend, y ofrece un servidor WSGI para migraciones desde Django/Flask y un servidor Rack para migraciones desde Rails
  • Actualmente está en estado beta y busca adopters tempranos; indica que la mayoría de las funciones están en buen estado y la documentación ya es utilizable

Framework web MVC para Rust

  • Rwf es un framework integral para construir aplicaciones web con Rust
  • Está escrito siguiendo el patrón clásico MVC (model-view-controller)
  • Su objetivo es incluir por defecto las funciones necesarias para crear fácilmente apps web rápidas y seguras
  • La documentación está disponible en la documentación oficial

Funcionalidades disponibles

  • Servidor y manejo de solicitudes
  • Datos y estructura de la aplicación
    • Ofrece un ORM fácil de usar para crear consultas PostgreSQL
    • Soporta migraciones de base de datos
    • Ofrece Dynamic templates
    • Incluye Authentication y sesiones de usuario integradas
  • API y procesamiento de tareas
  • Operación y herramientas de desarrollo
    • Soporta configuración por entorno
    • Ofrece logging y métricas
    • Incluye CLI

Soporte para Hotwired Turbo y migración

Inicio rápido

  • Crea una aplicación binaria de Rust y úsalo agregando rwf a las dependencias
cargo add rwf
  • La app básica usa rwf::prelude::* y rwf::http::Server para definir controladores y rutas
use rwf::prelude::*;
use rwf::http::Server;

#[controller]
async fn index() -> Response {
    Response::new().html("Welcome to Rwf!

")
}

#[tokio::main]
async fn main() {
    Server::new(vec![
        route!("/" => index),
    ])
    .launch()
    .await
    .unwrap();
}

Ejemplos y estado del proyecto

  • Los casos de uso comunes pueden verse en examples
  • Rwf está actualmente en estado beta y busca adopters tempranos
  • Indica que la mayoría de las funciones están en buen estado y la documentación ya es utilizable
  • Las contribuciones son bienvenidas; la guía está en CONTRIBUTING, la explicación de la estructura del código en ARCHITECTURE y la lista de funciones deseadas en ROADMAP

1 comentarios

 
GN⁺ 2024-10-23
Opiniones de Hacker News
  • El lanzamiento parece algo bastante irreal; felicitaciones. Lo más importante que aprendí tras mucho tiempo haciendo MVC web con Rails es que el framework debe contener con firmeza la lógica de negocio de la capa de modelo.
    Creo que conviene no poner en los modelos de datos callbacks como los que menciona Active Record, por ejemplo hooks como afterCreate. Si todavía no están en el ORM pero están en el roadmap, creo que sería mejor que no estuvieran.
    Eso sí, esto solo funciona bien cuando se recomienda con fuerza una capa de servicios, es decir, de lógica de negocio. La mayoría de mis apps Rails estructuran objetos de comando/servicio con gems como Interactor.
    Creo que solo con MVC y, por lo tanto, solo con la configuración básica de Rails, no es ideal escribir una app operable debido a la ausencia de una capa de servicios. También me da curiosidad por qué no se aprovechó algún ORM o query builder existente de la comunidad. Todavía no he escrito ni una línea de Rust, pero cada vez me da más curiosidad.

    • Me da curiosidad de dónde viene ese punto de vista. Mi opinión es exactamente la contraria: tanta lógica de negocio como sea posible debería estar en el modelo.
      Creo que los servicios deberían ser casi siempre fragmentos de código más complejos y específicos, disparados desde el modelo. Controladores delgados y modelos gruesos ha sido lo mejor para depurar, organizar y descubrir código; por experiencia, usar muchos servicios genera mucho código espagueti.
      Incluso desde una perspectiva puramente orientada a objetos, el modelo es el objeto básico que define la entidad. User debería saber todo lo que necesita saber sobre sí mismo y comunicarse con otras entidades mediante mensajes.
      Eso sí, estoy de acuerdo con lo de los callbacks. Los callbacks generan muchos efectos secundarios extraños y hacen que depurar sea realmente difícil.
    • Si no hay lógica de negocio, no sé qué sería exactamente un modelo. Suena como si quisieran una estructura donde a las structs se les llame modelos y a los procedimientos se les llame servicios.
      Se puede hacer así, pero se vuelve bastante difícil razonar sobre de qué maneras puede actualizarse una entidad. Porque cualquier cantidad de procedimientos podría actualizarla, y cada uno podría tener sus propias reglas de negocio. En ese caso, también podrían hacer que los procedimientos escriban directamente en la base de datos y eliminar el modelo.
    • Estoy de acuerdo en que la lógica de negocio no debería estar en el modelo. Martin Fowler y Robert C. Martin llaman a esto modelo de dominio anémico, y va en contra del enfoque de desarrollo empresarial del pasado, pesado en orientación a objetos y patrones.
      Pero después de más de 20 años desarrollando, llegué a la conclusión de que la encapsulación no es una funcionalidad sino una carga, y que los datos deben separarse de las operaciones que se realizan sobre ellos. A esto se le llama diseño orientado a datos o programación orientada a datos, y no soy la única persona que llegó a esa misma conclusión.
    • Decir que MVC por sí solo no alcanza para apps de nivel producción es una afirmación bastante fuerte. Personalmente no me gustan los objetos de servicio, porque quedan dispersos por todos lados y perjudican la capacidad de descubrir dónde está cada cosa.
      Hay otras formas de modelar que escalan bien, y también hay algunos artículos al respecto. Uno escrito por alguien de Basecamp es https://dev.37signals.com/vanilla-rails-is-plenty/.
      Claro que esto es muy orientado a objetos, así que tampoco me entusiasma demasiado. Por ejemplo, el framework Phoenix de Elixir usa contextos para agrupar funcionalidad relacionada, que en pocas palabras pueden verse como fachadas.
      Si te gustan los servicios, puedes usarlos, pero decir que MVC no es suficiente para producción me parece un poco errado. Manejar procesos de negocio pesados con callbacks del modelo no es bueno, pero para pequeñas tareas de moldear los datos en la forma correcta, funcionan bastante bien.
    • Interesante. En mi empresa hicimos nuestro propio ORM en PHP porque estaba prohibido usar librerías open source como Laravel, y sentí que los hooks eran muy útiles.
      Puede que esté sesgado, sobre todo porque mi experiencia de programación empezó con PHP para Wordpress, donde los hooks se usan extensamente.
      Lo que hice tiene una especificación de tablas que puede convertirse en definiciones SQL o generar formularios HTML con buen formato. Un controlador separado maneja todas las conexiones a la DB y las operaciones CRUD, y hay hooks antes y después que permiten, cuando hace falta, hacer referencias cruzadas fácilmente con otras tablas y restricciones.
      Funciona bastante bien, pero si pudiera me cambiaría de inmediato a Laravel.
  • Si es un framework REST, esperaría que por defecto pudiera generar definiciones Swagger/OpenAPI. Creo que esa es una de las funciones estrella de FastAPI.
    Tampoco termino de entender por qué hicieron su propio ORM en lugar de integrarse con un ORM existente como diesel.rs, ni por qué crearon su propio lenguaje de plantillas en vez de elegir un motor de plantillas popular ya existente.
    Fuera de eso, el proyecto se ve realmente interesante y pienso seguirlo de cerca.
    [0] https://diesel.rs/
    [1] https://crates.io/categories/template-engine

    • Probé Diesel hace unos años y me pareció demasiado a lo Rust para mi gusto. Había que definir el esquema dos veces, y como no se podía usar la misma struct para insertar y consultar, en la práctica terminaba siendo tres veces.
      En general fue un buen primer intento de adaptar los tipos de la base de datos a los tipos de Rust, pero un ORM debería ser más flexible y fácil de usar. La idea es que escribir queries sea más fácil, no más difícil :)
      En cuanto a las plantillas, hacer tu propio lenguaje es casi un rito de iniciación al club de nerds mayores de 30. No leí el Dragon Book, pero en la universidad siempre quise tomar esa clase. Siempre hay múltiples implementaciones de lo mismo, y se sintió correcto usar esta implementación que imita muy de cerca lo que hace ERB de Rails.
    • https://github.com/poem-web/poem es un framework Rust que trae definiciones Swagger de forma predeterminada.
    • Me pregunto si conocen algún framework similar que funcione al revés. Es decir, que mantenga la definición OpenAPI como fuente de verdad y valide que el servidor la cumpla.
    • Generar OpenAPI desde el servidor me parece 100% una función estrella. Nosotros lo usamos muy bien con Dropshot.
    • Yo también esperaría que un framework REST genere por defecto una definición Swagger/OpenAPI. Aunque, según entiendo, este no es un framework para construir APIs web, sino un framework MVC que devuelve HTML.
  • Desde la perspectiva de SRE, me interesó https://levkk.github.io/rwf/migrating-from-python/. Por un lado, es realmente genial que hayan logrado esto, pero desde el punto de vista de un SRE que mira la estabilidad, me hace gritar por dentro.
    Cuando la escala crece, esto debería manejarlo un proxy inverso como Caddy, Nginx o Traefik

    • Yo también pensé lo mismo, pero con este enfoque el ingeniero de aplicaciones puede probar los cambios localmente sin tener que ir y venir. Al final volvemos al viejo debate de monolito vs. microservicios.
      Es posible escribir un servidor WSGI estable, y no es tan difícil si se presta un poco de atención a detalles como la cantidad de hilos, vacuum, o reiniciar cada n solicitudes como el antiguo php-fpm. Básicamente, si implementas la mayoría de las opciones que tiene uwsgi, vas por el camino correcto. Está en el roadmap hacer que Rwf sea comparable a ejecutar Gunicorn
    • Supongo que el autor asumió que, si su audiencia es técnica, no hace falta explicar la necesidad de un proxy inverso delante del servidor WSGI
  • La elección del nombre es realmente increíble. Es una forma de quedar arriba en los resultados de búsqueda :P
    En serio, está bien hecho. Crear un buen framework es un trabajo sorprendentemente grande, y siempre es bueno ver a la gente explorar el espacio de diseño y probar ideas nuevas

    • Los proyectos de Rust tienden a elegir nombres amplios, genéricos y algo autobombos, y sinceramente creo que es un gran error. Crates.io se va a convertir en un cementerio de proyectos abandonados que ocuparon nombres decentes
    • “Row” también es un buen nombre, y creo que sería más fácil de pronunciar que rwf.
      RustOnWeb.com también se puede comprar por USD 10 :) Solo lo digo
  • Me gusta la expresión “piensa en Rails, no en Flask”. En los lenguajes con tipado estático hacen falta más, o mejores, frameworks con opiniones fuertes al estilo Rails/Django

  • Está bien hecho. Valdría la pena intentar que aparezca en https://www.arewewebyet.org/

    • Me sorprende bastante que loco.rs no esté en esa página. Parece el más cercano a “Rails” entre los frameworks de Rust, pero no aparece
  • ¡Bien! Django/Rails hecho con un lenguaje de primer nivel.
    Como sugerencia, async-trait ya debería estar estabilizado, así que quizá la macro ya no sea necesaria, y sería bueno agregar una integración con OpenTelemetry para obtener métricas y trazas por defecto. Para targets Linux también estaría bien usar jemalloc

    • Gracias. Intenté usar traits asíncronos estándar, pero no admiten el despacho dinámico que Rwf usa ampliamente.
      Planeo agregar etiquetas de OpenTelemetry a las funciones del crate tracing. jemalloc se puede agregar a las apps binarias que usan Rwf, y no hace falta incluirlo como dependencia de la biblioteca.
      [1] https://blog.rust-lang.org/2023/12/21/async-fn-rpit-in-trait...
    • Me da curiosidad por qué habría que usar jemalloc en targets Linux. O también por qué no sería necesario en otros targets
  • Intenté aprender Rust, pero abandoné al principio. Viniendo de C#, C/C++, Java y Python, Rust se me hizo raro.
    Lo que me venció no fue lo raro en sí, sino que, comparado con lenguajes como C#, Java y Python, la velocidad para terminar una tarea era menor.
    Hoy hago principalmente desarrollo web, así que la facilidad de uso y la productividad son lo más importante. Rust puede ser un poco mejor en throughput y velocidad, pero si miras benchmarks modernos de frameworks web, C# y Java también lo hacen bastante bien en ese aspecto.
    Espero que esto no se lea como una crítica a Rust ni como una forma de restarle mérito al gran logro de crear un framework MVC desde cero. Va más por el lado de “la herramienta adecuada para el trabajo”

    • Entiendo perfectamente lo que dices. Mucha gente, incluido yo, tuvo una experiencia parecida al principio.
      Para superar la curva de aprendizaje hace falta una motivación fuerte. Puede ser que tengas que usar el lenguaje, que te atraigan los beneficios del sistema de tipos y la seguridad, o que tengas alguna razón concreta propia.
      Si no tienes esa motivación, está totalmente bien. Yo tampoco la tenía al principio, y por eso mis primeros intentos de aprender no llegaron muy lejos. Como siente mucha gente, Rust toma más tiempo que Java o Python para terminar cosas, y exige pensar más por adelantado. En ciertas situaciones vale la pena, pero si no tienes claro por qué aceptar ese trade-off, no hay una razón fuerte para seguir
  • Hace poco dejé el loader JS de webpack y empecé a usar un loader en Rust 50 veces más rápido, y yo también me enganché con Rust. Rust tiene un gran impacto en la mejora de rendimiento, y ahora C++ y Rust son mis lenguajes favoritos

    • Oh, ¿podrías compartir qué estás usando después de dejar webpack?
  • Para trabajo de backend en Rust evalué todos los candidatos que suelen mencionarse e hice pequeños prototipos. Hasta ahora, aunque es más de bajo nivel de lo que me gustaría, terminé aceptando a regañadientes la conclusión colectiva de que Axum es la mejor opción en general.
    Los otros candidatos eran Loco y Rocket. Loco se parecía demasiado a Rails, y Rocket empezó a molestarme con sus macros recién después de usar más Rust.
    Este framework parece encajar perfectamente con mi criterio de “baterías incluidas, pero no demasiado prescriptivo”. Es una buena incorporación al ecosistema

    • ¿Qué tal Actix? Si mal no recuerdo, antes hubo una discusión en HN sobre Actix y Axum