3 điểm bởi GN⁺ 2024-10-23 | 1 bình luận | Chia sẻ qua WhatsApp
  • Rwf là một framework toàn diện để xây dựng ứng dụng web bằng Rust, dựa trên mẫu MVC cổ điển và cung cấp sẵn các tính năng cần thiết để tạo web app nhanh và an toàn
  • Các tính năng mặc định bao gồm máy chủ HTTP, ORM cho truy vấn PostgreSQL, template động, xác thực và phiên người dùng, middleware, tác vụ nền và tác vụ theo lịch, cùng migration cơ sở dữ liệu
  • Cũng cung cấp REST framework tích hợp hỗ trợ tuần tự hóa JSON, WebSockets, hosting tệp tĩnh, cấu hình theo môi trường, logging và metrics, cùng CLI
  • Tích hợp chặt chẽ với Hotwired Turbo để tạo SPA do backend điều khiển, đồng thời cung cấp máy chủ WSGI để migrate từ Django/Flask và máy chủ Rack để migrate từ Rails
  • Hiện đang ở trạng thái beta và tìm kiếm những người dùng sớm; tác giả cho biết phần lớn tính năng đã ở trạng thái tốt và tài liệu đủ để sử dụng

Framework web MVC cho Rust

  • Rwf là một framework toàn diện để xây dựng ứng dụng web bằng Rust
  • Được viết theo mẫu MVC(model-view-controller) cổ điển
  • Hướng tới việc mặc định bao gồm các tính năng cần thiết để dễ dàng tạo web app nhanh và an toàn
  • Tài liệu được cung cấp tại tài liệu chính thức

Tính năng cung cấp

  • Máy chủ và xử lý request
  • Dữ liệu và cấu trúc ứng dụng
    • Cung cấp ORM thân thiện với người dùng để dễ dàng tạo truy vấn PostgreSQL
    • Hỗ trợ migration cơ sở dữ liệu
    • Cung cấp Dynamic templates
    • Bao gồm Authentication và phiên người dùng tích hợp
  • API và xử lý tác vụ
  • Vận hành và công cụ phát triển
    • Hỗ trợ cấu hình theo môi trường
    • Cung cấp logging và metrics
    • Bao gồm CLI

Hỗ trợ Hotwired Turbo và migrate

Bắt đầu nhanh

  • Tạo một ứng dụng nhị phân Rust và thêm rwf vào dependencies để sử dụng
cargo add rwf
  • Ứng dụng cơ bản dùng rwf::prelude::*rwf::http::Server để định nghĩa controller và route
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();
}

Ví dụ và trạng thái dự án

  • Có thể xem các trường hợp sử dụng phổ biến trong examples
  • Rwf hiện đang ở trạng thái beta và đang tìm kiếm những người dùng sớm
  • Tác giả cho biết phần lớn tính năng đã ở trạng thái tốt và tài liệu đủ để sử dụng
  • Rất hoan nghênh đóng góp; hướng dẫn có tại CONTRIBUTING, hướng dẫn cấu trúc mã tại ARCHITECTURE, và danh sách tính năng mong muốn tại ROADMAP

1 bình luận

 
GN⁺ 2024-10-23
Các ý kiến trên Hacker News
  • Việc ra mắt có vẻ sẽ tạo cảm giác khá phi thực tế, xin chúc mừng. Điều lớn nhất tôi học được sau thời gian dài làm web MVC với Rails là framework phải kiềm chế mạnh logic nghiệp vụ ở tầng model
    Tôi nghĩ tốt nhất là không đưa các callback mà Active Record nói tới vào model dữ liệu, chẳng hạn các hook như afterCreate. Hiện ORM chưa có, nhưng nếu nó nằm trong roadmap thì tôi nghĩ không có còn hơn
    Tuy nhiên điều này chỉ hoạt động đúng khi service, tức tầng logic nghiệp vụ, được khuyến khích mạnh mẽ. Hầu hết các ứng dụng Rails của tôi cấu trúc command/service object bằng các gem như Interactor
    Tôi cho rằng chỉ MVC, và do đó chỉ cấu hình mặc định của Rails, không lý tưởng để viết ứng dụng có thể vận hành được vì thiếu tầng service. Và tôi cũng tò mò vì sao không tận dụng ORM hoặc query builder hiện có của cộng đồng. Tôi vẫn chưa viết một dòng Rust nào, nhưng ngày càng tò mò hơn

    • Tôi tò mò quan điểm này xuất phát từ đâu. Suy nghĩ của tôi thì hoàn toàn ngược lại: càng nhiều logic nghiệp vụ càng nên nằm trong model
      Tôi nghĩ service hầu như chỉ nên là những đoạn mã cụ thể phức tạp hơn được kích hoạt từ model. Controller mỏng, model dày là tốt nhất về mặt debug, tổ chức và khả năng khám phá; còn dùng nhiều service thì theo kinh nghiệm của tôi dễ sinh ra rất nhiều spaghetti code
      Ngay cả từ góc nhìn hướng đối tượng thuần túy, model là đối tượng nền tảng định nghĩa entity. User nên biết mọi thứ cần biết về chính nó, và giao tiếp với các entity khác bằng message
      Tuy nhiên tôi đồng ý về callback. Callback tạo ra nhiều tác dụng phụ kỳ lạ và khiến việc debug thực sự khó khăn
    • Nếu không có logic nghiệp vụ thì tôi không biết rốt cuộc model là gì. Nghe như bạn muốn một cấu trúc gọi struct là model và gọi procedure là service
      Có thể làm như vậy, nhưng sẽ khá khó suy luận entity có thể được cập nhật bằng những cách nào. Vì bất kỳ số lượng procedure nào cũng có thể cập nhật nó và mỗi cái có thể có quy tắc nghiệp vụ riêng. Nếu vậy thì cứ để procedure ghi trực tiếp vào cơ sở dữ liệu và bỏ model đi cũng được
    • Tôi đồng ý rằng model không nên có logic nghiệp vụ. Martin Fowler và Robert C. Martin gọi kiểu này là anemic domain model, và nó trái với cách phát triển enterprise nặng về hướng đối tượng và pattern trước đây
      Nhưng sau hơn 20 năm phát triển phần mềm, tôi đi đến kết luận rằng đóng gói là gánh nặng chứ không phải tính năng, và dữ liệu nên được tách khỏi các thao tác thực hiện trên dữ liệu đó. Điều này được gọi là thiết kế hướng dữ liệu hoặc lập trình hướng dữ liệu, và không chỉ mình tôi đi đến kết luận này
    • Nói rằng chỉ MVC là không đủ cho ứng dụng cấp vận hành là một tuyên bố khá lớn. Cá nhân tôi không thích service object, vì chúng rải rác khắp nơi và làm giảm khả năng khám phá
      Cũng có những cách modeling khác mở rộng tốt, và có vài bài viết liên quan. Có bài của người Basecamp: https://dev.37signals.com/vanilla-rails-is-plenty/
      Tất nhiên cách này rất hướng đối tượng nên tôi cũng không quá thích. Ví dụ framework Phoenix của Elixir dùng context để gom các chức năng liên quan, nói đơn giản có thể xem như facade
      Nếu thích service thì có thể dùng, nhưng nói MVC không đủ cho cấp production thì tôi thấy hơi lệch. Xử lý các quy trình nghiệp vụ nặng bằng model callback là không tốt, nhưng với các việc nhỏ để chỉnh dữ liệu về đúng dạng thì khá ổn
    • Thú vị đấy. Ở công ty tôi từng tự làm PHP ORM, vì bị cấm dùng thư viện mã nguồn mở như Laravel, và tôi thấy hook rất hữu ích
      Có lẽ tôi cũng thiên lệch vì kinh nghiệm lập trình của tôi bắt đầu từ PHP cho Wordpress, nơi dùng hook rất rộng rãi
      Thứ tôi làm có đặc tả bảng có thể chuyển thành định nghĩa SQL hoặc xuất ra form HTML được định dạng đẹp. Một controller riêng xử lý mọi kết nối DB và thao tác CRUD, kèm các hook trước/sau để dễ dàng tham chiếu chéo với bảng khác và ràng buộc khi cần
      Nó hoạt động khá tốt, nhưng nếu có thể thì tôi sẽ chuyển sang Laravel ngay lập tức
  • Nếu là REST framework thì mặc định tôi kỳ vọng nó có thể tạo định nghĩa Swagger/OpenAPI. Tôi xem đó là một trong những killer feature của FastAPI
    Tôi cũng không hiểu rõ vì sao lại tự làm ORM thay vì tích hợp với ORM hiện có như diesel.rs, và vì sao tự tạo ngôn ngữ template thay vì chọn một template engine hiện có phổ biến
    Ngoài ra thì dự án trông thật sự thú vị và tôi sẽ tiếp tục theo dõi
    [0] https://diesel.rs/
    [1] https://crates.io/categories/template-engine

    • Tôi từng dùng Diesel vài năm trước và với tôi nó cảm giác quá kiểu Rust. Phải định nghĩa schema hai lần, và không thể dùng cùng một struct cho insert và select, nên thực tế thành ra định nghĩa ba lần
      Nhìn chung đó là một nỗ lực đầu tiên tốt để khớp kiểu dữ liệu cơ sở dữ liệu với kiểu Rust, nhưng ORM cần linh hoạt hơn và dễ dùng hơn. Vì nó phải làm cho việc viết query dễ hơn, chứ không phải khó hơn :)
      Về template, việc tự tạo ngôn ngữ của riêng mình gần như là nghi thức gia nhập câu lạc bộ nerd trên 30 tuổi. Tôi chưa đọc Dragon Book, nhưng hồi đi học rất muốn được học môn đó. Cùng một thứ luôn có nhiều cách triển khai, và tôi thấy việc dùng triển khai này, vốn bắt chước rất sát những gì ERB của Rails làm, là hợp lý
    • https://github.com/poem-web/poem là một framework Rust cung cấp sẵn định nghĩa Swagger
    • Tôi tò mò không biết có ai biết framework tương tự nhưng hoạt động theo chiều ngược lại không. Ý tôi là giữ định nghĩa OpenAPI làm nguồn sự thật, rồi xác minh server có tuân theo nó hay không
    • Tôi cho rằng việc tạo OpenAPI từ server 100% là killer feature. Chúng tôi đang dùng Dropshot khá tốt
    • Tôi cũng kỳ vọng REST framework mặc định sẽ tạo định nghĩa Swagger/OpenAPI. Tuy nhiên theo tôi hiểu thì đây không phải framework để xây dựng web API, mà là framework MVC trả về HTML
  • Ở góc nhìn SRE, tôi thấy quan tâm đến https://levkk.github.io/rwf/migrating-from-python/. Một mặt, việc làm được điều này thật sự rất ấn tượng, nhưng từ góc nhìn SRE coi trọng độ ổn định thì trong lòng tôi phải hét lên
    Khi quy mô lớn lên, việc này nên do reverse proxy như Caddy, Nginx, Traefik xử lý

    • Tôi cũng nghĩ như vậy, nhưng với cách này thì kỹ sư ứng dụng có thể kiểm thử thay đổi ở local mà không phải qua lại nhiều lần. Cuối cùng lại quay về cuộc tranh luận cũ monolith vs microservices
      Viết một WSGI server ổn định là có thể, và cũng không quá khó nếu chỉ cần chú ý một chút đến các chi tiết như số luồng, vacuum, hay khởi động lại sau mỗi n request như php-fpm ngày xưa. Về cơ bản, nếu triển khai phần lớn các tùy chọn mà uwsgi có thì xem như đang đi đúng hướng. Trong roadmap có việc làm cho Rwf có thể so sánh với việc chạy Gunicorn
    • Có lẽ tác giả giả định rằng nếu độc giả là người kỹ thuật thì không cần phải nói rõ sự cần thiết của reverse proxy phía trước WSGI server
  • Cách chọn tên thật sự đỉnh. Đó cũng là một cách để leo lên đầu kết quả tìm kiếm :P
    Nói nghiêm túc thì làm rất tốt. Tạo ra một framework tốt là một công việc lớn đến đáng ngạc nhiên, và luôn thú vị khi thấy mọi người khám phá không gian thiết kế và thử các ý tưởng mới

    • Các dự án Rust có xu hướng chọn những cái tên rộng, chung chung và hơi phô trương như thế này; thành thật mà nói, tôi nghĩ đó là một sai lầm lớn. Crates.io rồi sẽ trở thành nghĩa địa của những dự án bị bỏ hoang nhưng đã chiếm trước các tên nghe có vẻ hay
    • “Row” cũng là một cái tên hay, và có vẻ dễ nói hơn rwf
      RustOnWeb.com cũng có thể mua với giá $10 :) Chỉ nói vậy thôi
  • Tôi thích cách diễn đạt “hãy nghĩ đến Rails, không phải Flask”. Các ngôn ngữ kiểu tĩnh cần có nhiều hơn hoặc tốt hơn những framework có chính kiến kiểu Rails/Django

  • Làm tốt lắm. Rất đáng thử để được giới thiệu trên https://www.arewewebyet.org/

    • Khá ngạc nhiên là trang này không có loco.rs. Dù nó trông là framework Rust gần với “Rails” nhất nhưng lại vắng mặt
  • Hay! Django/Rails được làm bằng một ngôn ngữ hạng nhất
    Góp ý là async-trait giờ chắc đã ổn định rồi nên có thể không cần macro nữa, và sẽ tốt nếu thêm tích hợp OpenTelemetry để mặc định có metrics và tracing. Với target Linux thì cũng nên dùng jemalloc

    • Cảm ơn. Tôi đã thử dùng trait bất đồng bộ chuẩn, nhưng nó không hỗ trợ dynamic dispatch mà Rwf dùng rộng rãi
      Tôi dự định thêm các tag OpenTelemetry vào các hàm của crate tracing. jemalloc có thể được thêm vào ứng dụng nhị phân sử dụng Rwf, không cần đưa vào như dependency của thư viện
      [1] https://blog.rust-lang.org/2023/12/21/async-fn-rpit-in-trait...
    • Tôi tò mò vì sao nên dùng jemalloc cho target Linux. Hoặc vì sao các target khác lại không cần
  • Tôi đã thử học Rust nhưng bỏ cuộc ngay từ đầu. Từ C#, C/C++, Java, Python chuyển sang nên Rust cảm giác rất lạ
    Thứ khiến tôi nản không phải bản thân sự lạ lẫm, mà là tốc độ hoàn thành một công việc chậm hơn so với các ngôn ngữ như C#, Java, Python
    Dạo này tôi chủ yếu làm web development nên tính dễ dùng và năng suất là ưu tiên hàng đầu. Rust có thể nhỉnh hơn một chút về throughput và tốc độ, nhưng nhìn các benchmark framework web hiện đại thì C# và Java cũng làm khá tốt ở mặt này
    Tôi không muốn điều này bị hiểu là phê phán Rust hay hạ thấp thành tựu lớn là tạo một MVC framework từ đầu. Nó gần với ý “công cụ phù hợp cho công việc phù hợp” hơn

    • Tôi hoàn toàn hiểu ý bạn. Nhiều người, trong đó có tôi, lúc đầu cũng có trải nghiệm tương tự
      Để vượt qua đường cong học tập cần có động lực mạnh. Có thể là bạn phải dùng ngôn ngữ đó, hoặc bị thu hút bởi lợi ích của type system và độ an toàn, hoặc có lý do cụ thể của riêng mình
      Nếu không có động lực như vậy thì hoàn toàn ổn. Ban đầu tôi cũng không có, nên các lần thử học đầu tiên không đi xa. Như nhiều người cảm nhận, Rust mất nhiều thời gian hơn Java hay Python để hoàn thành việc, và đòi hỏi suy nghĩ nhiều hơn từ phía trước. Trong một số tình huống thì điều đó đáng giá, nhưng nếu không rõ vì sao mình nên chấp nhận trade-off đó thì không có lý do mạnh để tiếp tục
  • Gần đây tôi bỏ loader JS của webpack và dùng một loader Rust nhanh hơn 50 lần, nên tôi cũng đã mê Rust. Rust đem lại cải thiện hiệu năng rất lớn, và hiện giờ C++ và Rust là hai ngôn ngữ tôi thích nhất

    • Ồ, bạn có thể chia sẻ là đã bỏ webpack để dùng gì không?
  • Tôi đã đánh giá tất cả các ứng viên thường được nhắc đến cho công việc backend Rust và làm các prototype nhỏ. Đến giờ, dù vẫn ở mức thấp hơn mong muốn, tôi miễn cưỡng đồng ý với kết luận chung rằng nhìn tổng thể Axum là lựa chọn tốt nhất
    Các ứng viên khác là Loco và Rocket. Loco thì quá giống Rails, còn Rocket thì sau khi dùng Rust nhiều hơn, các macro bắt đầu khiến tôi khó chịu
    Framework này có vẻ khớp hoàn hảo với tiêu chí của tôi là “có sẵn pin nhưng không quá áp đặt”. Một bổ sung tốt cho hệ sinh thái

    • Actix thì sao? Theo tôi nhớ, trước đây trên HN từng có thảo luận liên quan đến Actix và Axum