7 điểm bởi GN⁺ 2023-06-28 | 1 bình luận | Chia sẻ qua WhatsApp
  • ORM thường bị chỉ trích là “đồ chơi cho startup”, nhưng vấn đề thực sự nằm ở cách sử dụng sai hơn là bản thân ORM
  • Đồ thị đối tượng và bảng quan hệ là hai mô hình khác nhau nên sẽ phát sinh sự không tương thích mô hình, nhưng chỉ riêng điều này khó có thể xem ORM là anti-pattern
  • Tranh luận về việc vi phạm SRP và SOC là có cơ sở, nhưng ORM vốn là một lớp trừu tượng để kết nối hai mô hình dữ liệu, nên khó đánh giá chỉ dựa trên việc có vi phạm nguyên tắc hay không
  • Vấn đề hiệu năng thường phát sinh từ pattern kết hợp dữ liệu bằng logic của ngôn ngữ host hơn là từ ORM, và có thể giảm bớt bằng các tính năng giống SQL của ORM
  • Lý do cốt lõi khiến Lago chuyển các truy vấn Active Record sang SQL thô không phải là tốc độ mà là khả năng quan sát và debug; với các truy vấn lớn, chuyển sang SQL thô có thể có lợi trong thực tế

Quan điểm cơ bản khi nhìn nhận các chỉ trích về ORM

  • ORM thường bị công kích là “anti-pattern”, nhưng kết luận rằng bản thân ORM là xấu thì khá phóng đại
  • Cũng như các lớp trừu tượng khác, ORM không hoàn hảo và có thể gây ra một số mất mát về khả năng quan sát cùng chi phí hiệu năng
  • Lago đã gặp vấn đề khi phụ thuộc vào Active Record, ORM của Ruby on Rails, và từ đó xem xét lại cách phụ thuộc vào ORM
  • Kết luận nghiêng về việc nên xem ORM là một lớp trừu tượng có ưu và nhược điểm rõ ràng, chứ không phải là phải loại bỏ ORM

Khác biệt giữa mô hình đối tượng và cơ sở dữ liệu quan hệ

  • Các đối tượng mà ORM xử lý gần với đồ thị có hướng, trong đó các nút trỏ đến nút khác
  • Bảng trong cơ sở dữ liệu quan hệ gần với đồ thị vô hướng, trong đó dữ liệu được liên kết hai chiều thông qua khóa chung
  • ORM có thể mô phỏng đồ thị vô hướng, nhưng cấu hình và hành vi không phải lúc nào cũng đơn giản
    • Đối tượng User có thể thiếu mảng Posts
    • Các thực thể trong mảng Posts có thể không có tham chiếu ngược về cùng đối tượng User
    • Ngay cả khi có tham chiếu ngược, đó có thể là một đối tượng được sao chép chứ không phải cùng một đối tượng
  • Ví dụ, ORM có thể trả về các bài viết của người dùng dưới dạng mảng, nhưng không đưa vào từng bài viết tham chiếu ngược đến người dùng trong vai trò tác giả
  • Sự không tương thích này là một phê bình mang tính học thuật hợp lý đối với ORM, nhưng trong vận hành thực tế, vấn đề lớn hơn thường bộc lộ ở cách sử dụng cụ thể

Tranh luận về SRP và SOC

  • Nguyên tắc đơn trách nhiệm (SRP)

    • ORM bị chỉ trích là vi phạm nguyên tắc đơn trách nhiệm (SRP)
    • Vì nó có thể khiến một lớp hoặc một tầng đảm nhận nhiều vai trò cùng lúc
      • Thực hiện cơ sở dữ liệu và transaction
      • Biểu diễn record
      • Định nghĩa quan hệ
      • Tạo và chạy migration
    • Tuy nhiên, mục đích của ORM vốn là kết nối hai mô hình dữ liệu khác nhau, nên khó phủ nhận ORM chỉ vì một số vi phạm nguyên tắc
  • Tách biệt mối quan tâm (SOC)

    • ORM cũng có thể làm mờ nguyên tắc tách biệt mối quan tâm (SOC)
    • SOC là nguyên tắc cho rằng một thành phần hạ tầng chỉ nên có một mối quan tâm
    • Vì ORM chuyển việc quản lý cơ sở dữ liệu sang phía backend, nên có thể xem là có khả năng vi phạm SOC
    • Tuy vậy, ngày nay các thành phần hạ tầng và pattern lập trình cũng thường kết hợp nhiều vai trò vì hiệu năng, độ trễ và tổ chức code
      • Bộ tổng hợp CPU trong cơ sở dữ liệu OLAP
      • Backend-frontend ở edge
      • Monorepo

Vấn đề hiệu năng thường đến từ cách sử dụng hơn là từ ORM

  • Chỉ trích rằng ORM luôn kém hiệu quả phần lớn là sai
  • Nhiều ORM có thể hoạt động hiệu quả hơn rất nhiều so với suy nghĩ của lập trình viên
  • Vấn đề là ORM khiến việc kết hợp dữ liệu bằng logic của ngôn ngữ host như JavaScript hay Ruby trở nên dễ dàng
  • Trong ví dụ TypeORM, pattern xấu là truy vấn trước các tác giả thuộc một công ty nhất định, rồi truy vấn lại bài viết của từng tác giả, sau đó lưu riêng từng bài viết
  • Cách tốt hơn là dùng createQueryBuilder() của TypeORM để xây dựng một truy vấn cập nhật duy nhất
  • Trong lần refactor SQL tính phí của Lago, cũng không có khác biệt hiệu năng giữa phiên bản Active Record và SQL thô
    • Vì đã sử dụng nhiều tính năng kết hợp dữ liệu của Active Record, nên truy vấn cũ vốn đã ở trạng thái được tối ưu
    • Việc viết lại bằng SQL thô nghiêng về mục tiêu đảm bảo khả năng quan sát hơn là cải thiện hiệu năng

Khi ORM thực sự có thể trở nên chậm

  • ORM không phải lúc nào cũng hiệu quả như SQL thô, và trong một số tình huống có thể khá kém hiệu quả
  • Vấn đề đầu tiên là ORM có thể tạo ra overhead tính toán lớn khi chuyển đổi kết quả truy vấn thành đối tượng
    • TypeORM được nhắc đến như một ví dụ cho vấn đề này
  • Vấn đề thứ hai là khi duyệt quan hệ một-nhiều hoặc nhiều-nhiều và gửi nhiều lượt request khứ hồi tới cơ sở dữ liệu
  • Pattern này được gọi là vấn đề N+1
    • Thêm N truy vấn con vào 1 truy vấn ban đầu
    • Trong ví dụ Prisma, khi truy vấn lồng nhau người dùng, bài viết và bình luận, mỗi bình luận có thể phát sinh một request cơ sở dữ liệu mới
  • N+1 thường xuất hiện trong ORM, nhưng nếu dùng data loader thì có thể gộp N+1 truy vấn thành 2 truy vấn
  • Phần lớn các vấn đề phổ biến của ORM có thể tránh được nếu tận dụng đầy đủ các tính năng của ORM

Vấn đề lớn hơn là khả năng quan sát và debug

  • Điểm yếu lớn nhất của ORM là khả năng quan sát
  • Vì ORM về bản chất là trình xây dựng truy vấn, nên ngoài các trường hợp như lỗi kiểu dữ liệu thô rõ ràng, nó không phải là bên truyền lỗi cuối cùng
  • Khi SQL trả về lỗi, ORM phải diễn giải lỗi đó rồi truyền lại cho người dùng
  • Active Record gặp khó ở điểm này, và đây cũng là lý do Lago refactor truy vấn đăng ký tính phí
  • Khi kết quả khác với kỳ vọng, phải lặp lại quy trình sau
    • Kiểm tra truy vấn SQL đã được render
    • Chạy lại SQL đó
    • Chuyển lỗi SQL trở lại thành thay đổi trong Active Record
  • Quy trình qua lại như vậy làm suy yếu mục đích ban đầu của việc dùng Active Record để tránh phải tương tác trực tiếp với cơ sở dữ liệu SQL

Đánh giá thực tiễn

  • Nếu dùng đúng cách, ORM có thể đạt hiệu quả gần với SQL thô
  • Nhiều vấn đề phát sinh khi không dùng các tính năng giống SQL của ORM mà phụ thuộc quá nhiều vào cấu trúc logic của ngôn ngữ host
  • Nếu các truy vấn lớn gây khó cho lập trình viên và cản trở việc debug, chuyển sang truy vấn SQL thô có thể là một khoản đầu tư đáng giá
  • Hầu hết ORM đều cung cấp chức năng chạy truy vấn SQL bên trong ORM

1 bình luận

 
GN⁺ 2023-06-28
Ý kiến trên Hacker News
  • Tôi đã dùng ORM từ khi Hibernate vừa mới xuất hiện trên Java, nhưng lúc nào cũng thấy nó dở
    Cái lợi ích kiểu “có thể đổi sang cơ sở dữ liệu khác” giờ nhìn lại gần như là điều nhảm nhí, và trên thực tế chẳng ai dùng như vậy cả
    Nói rằng “không cần biết SQL” cũng sai, và với những ứng dụng không tầm thường sống lâu thì cuối cùng vẫn phải chỉnh từng truy vấn ở mức chuỗi
    Tôi cho rằng tầng dữ liệu nên được làm theo kiểu tạo và nội suy từng truy vấn bằng SQL dạng chuỗi, và càng gần với raw JDBC thì càng tốt
    Lập luận rằng ORM hỗ trợ “mô hình miền” cũng là một lý do tệ, vì mô hình miền đó rốt cuộc lúc nào cũng trở thành mô hình miền nghèo nàn với logic trống rỗng
    Nghĩ đến khoảng thời gian bị lãng phí vào ORM, XML, annotation và việc debug SQL được sinh ra là chỉ muốn khóc

    • Hoàn toàn đồng ý. Tôi muốn diễn đạt ORM là thứ “làm phần dễ trở nên dễ hơn một chút, nhưng làm phần khó trở nên cực kỳ khó”
      Phần dễ vốn đã dễ rồi nên tôi không mấy quan tâm, mà ngược lại đã có rất nhiều lần công việc khó hơn gấp triệu lần vì phải chiến đấu với chính ORM vào đúng khoảnh khắc tệ nhất, khi truy vấn đổ vỡ vì quy mô
      Query builder không hoàn toàn giống ORM, nhưng tôi thích bài https://gajus.medium.com/stop-using-knex-js-and-earn-30-bf41... và trở thành fan của những công nghệ kiểu Slonik
      Nó cung cấp 90% những gì tôi muốn, như tự động bind kết quả vào object và kiểm tra kiểu mạnh, nhưng vẫn cho phép chỉ đơn giản viết SQL bình thường
      Tôi cũng không hiểu nổi suy nghĩ cho rằng SQL là một ngôn ngữ thần bí khó nhằn. Nó cũ kỹ nên có khuyết điểm, nhưng học SQL vẫn tốt hơn là học ngôn ngữ truy vấn riêng do công cụ ORM tạo ra
    • Ngay từ lần đầu dùng từ rất lâu trước đây, tôi đã ghét ORM vì những lý do đó và xem nó là phản mẫu
      Hồi đó nếu nói ra những lời chỉ trích kiểu này thì các lập trình viên khác thường nhìn mình bằng nửa con mắt
      Đến giờ vẫn có rất nhiều công cụ và kỹ thuật mà với tư cách lập trình viên tôi buộc phải dùng dù chúng là phản mẫu, và thực tế đáng buồn của ngành là không ít công cụ được dùng rộng rãi lại dở và còn khuyến khích phản mẫu
      Trong ngành công nghệ có một số ít lập trình viên và cựu lập trình viên có quan hệ rộng nắm quyền quyết định, và tôi cảm thấy họ cũng không ra gì
    • Bạn lại khởi đầu đúng bằng một trong những ORM tệ nhất hiện có
      Cũng có những ORM có tỷ lệ ưu và nhược điểm khá ổn, đặc biệt là phía Python
      Django ORM thì thô kệch và hiệu năng không tốt, nhưng được tích hợp tốt, thực dụng, năng suất và cảm giác dùng cũng ổn
      Peewee cung cấp ORM trong một gói nhỏ, nên khi không cần gì ghê gớm và chỉ muốn đỡ phiền thì nó giúp viết các chương trình nhỏ một cách vui vẻ
      SQLAlchemy đòi hỏi đầu tư nhiều hơn nhưng rất linh hoạt, sinh ra SQL sạch sẽ và hoạt động chính xác
      Nó cũng lộ ra các query builder cấp thấp hơn dành cho khi bạn không muốn mô hình hướng đối tượng mà muốn biểu đạt hành vi SQL mang tính thành ngữ dưới dạng trừu tượng bằng Python
      Cuối cùng thì đây vẫn là một quyết định kỹ thuật thông thường dựa trên lợi tức trên đầu tư
    • Câu “phải chỉnh ở mức chuỗi” theo kinh nghiệm của tôi là nhảm nhí
      Tôi đã làm việc này hơn 10 năm, nhưng chưa có truy vấn nào mà chỉ cần đọc tài liệu Hibernate 5 phút và làm đúng theo hướng dẫn cho tình huống đó lại không xử lý được tốt hơn
      Có vẻ như những fanboy SQL tin rằng mọi lập trình viên phải thuộc lòng bảng logic ba giá trị và các tên hàm kiểu COBOL, nhưng lại không muốn bỏ công đọc tài liệu Hibernate
    • “Không cần biết SQL” chưa bao giờ là ưu điểm của ORM đối với bất kỳ ai có chút suy nghĩ
      ORM dành cho OLTP workload, không phải cho OLAP, và một trong những người tạo ra Hibernate cũng từng nói như vậy
      Lợi ích chính chỉ là giúp khỏi phải tự viết những câu lệnh INSERT/UPDATE dài dòng dễ lỗi, đồng thời tự động ánh xạ các hàng SQL thành object
      Nếu đi theo kiểu raw thì bạn sẽ phải tự triển khai lại khá nhiều phần trong số đó, nên đây cũng không phải trò chơi có tổng bằng không
  • Trong những ứng dụng đủ phức tạp, sớm muộn gì cũng sẽ xuất hiện thứ gì đó như query builder để nhiều phần của ứng dụng có thể cùng nhau kết hợp truy vấn theo kiểu trì hoãn
    Đây là cấu trúc hướng tới việc kết hợp bằng truy vấn thay vì ghép, lọc và sắp xếp dữ liệu trong bộ nhớ
    ORM là công cụ giúp làm điều này dễ dàng, nhưng cũng có thể vô tình khiến bạn tự bắn vào chân vì rất dễ tạo ra truy vấn N+1 mà không nhận ra
    Giống như mọi công cụ khác, cần cân nhắc trade-off cùng với trường hợp sử dụng thực tế, và tôi không khuyến nghị cách tự thao tác trực tiếp với chuỗi câu lệnh SQL trong bất kỳ trường hợp nào

    • Trải nghiệm với ORM có thể khác nhau cực đoan tùy theo ngôn ngữ dùng, mức độ tích hợp và loại công việc
      Dù vậy, lúc nào cũng có người bỏ qua những khác biệt đó rồi khẳng định một cách tuyệt đối rằng ORM là tốt hay xấu
      Tốt hơn là cứ xem đó như một thái độ gây tranh cãi để thu hút chú ý rồi bỏ qua, và tập trung vào những thảo luận bắt đầu từ quan điểm “ORM có lợi ích, nhưng cần biết nó hiệu quả ở đâu và đừng để vấn đề khi sử dụng lấn át lợi ích đó”
    • Tôi không xem chuyện ứng dụng đủ phức tạp thì sẽ cần query builder là điều hiển nhiên, và trong ứng dụng của tôi thì tôi không chấp nhận điều đó
      Khi cần dữ liệu thì hãy đi qua tầng truy cập dữ liệu, và nếu nó chưa cung cấp thứ cần thiết thì hãy tạo repository, provider mới hoặc thứ gì đó phù hợp với pattern đang dùng
      Dù là ORM hay bộ kết hợp truy vấn tự viết, nếu ứng dụng đã đến mức tự lừa mình rằng “nó đủ phức tạp nên cần cái này”, thì thực ra nó đã quá phức tạp để vận hành ổn định rồi
      Cuối cùng bạn либо đang đánh giá builder ở sai tầng, hoặc sẽ bỏ lỡ việc mình đang tạo ra truy vấn N+1 trong lúc lặp qua kết quả
      ORM là không cần thiết
    • Tôi thấy điều này gần với những gì LINQ làm từ năm 2007
      LINQ không hẳn chính xác là ORM; khi bạn tạo các câu LINQ được định kiểu mạnh, chúng sẽ được chuyển thành cây biểu thức, rồi nhà cung cấp truy vấn sẽ biến chúng thành truy vấn
    • Với tư cách người dùng cả ORM lẫn raw SQL, tôi thấy đồng cảm với cả hai phía
      Trong lúc phát triển, tôi thích tính dễ đọc của ORM trong model, nhưng khi cần thì sẽ theo dõi sát N+1 và các dạng kém hiệu quả khác để tối ưu
      Ranh giới của “khi cần” không rõ ràng, nhưng tôi cố tránh tối ưu quá sớm
      Với dự án mới, tôi thường bắt đầu từ cơ sở dữ liệu và nắm cảm giác về mô hình dữ liệu bằng factory, seeder và truy vấn raw SQL trước khi viết code
      ORM như Eloquent của Laravel có những phương thức tốt để xử lý N+1 và thực hiện lazy loading, nhưng lúc nào cũng có trade-off
    • Cũng có những thư viện lật ngược khái niệm ORM
      Thay vì thư viện cho phép kết hợp truy vấn trì hoãn, bạn tự viết truy vấn và thư viện sẽ sinh mã cho truy vấn đó ở thời điểm biên dịch
      Ví dụ, nếu bạn viết truy vấn như getUser: SELECT * FROM users WHERE id = ? thì nó sẽ tạo ra lớp GetUserQuery và phương thức như getUser(id: String): GetUserQueryResult, và tôi thấy mô hình này tốt hơn nhiều
  • Lý do chính tôi không thích ORM là vì chúng có xu hướng đối xử với các SQL engine hiện đại như một kho lưu bit ngu ngốc bị phóng đại
    CTE, LATERAL join, mệnh đề RETURNING có thể đơn giản hóa xử lý rất nhiều hoặc loại bỏ khả năng dữ liệu không nhất quán, nhưng ORM phần lớn chỉ dừng lại ở mức ánh xạ đơn giản các bảng và view cơ bản sang những object được định nghĩa sẵn
    Nếu ORM còn tự tạo cả bảng thì còn tệ hơn
    SQL về bản chất vừa là công cụ trích xuất dữ liệu vừa là ngôn ngữ biến đổi, nhưng ORM gần như phớt lờ mặt này, khiến nhiều lập trình viên không biết rằng SQL còn có nhiều thứ hơn INSERT/SELECT/UPDATE/DELETE
    Còn có bảng pivot, truy vấn theo thời gian, CUBE/ROLLUP, window function, set-returning function, materialized view, external table, xử lý JSON, xử lý ngày tháng, exclusion constraint, các kiểu như range/interval/domain, bảo mật mức hàng, MERGE, v.v.
    Giống như có sẵn cả một nhà kho đầy công cụ mà ai đó chỉ đưa cho bạn mỗi cái búa, tua vít và cưa sắt rồi thuyết phục rằng thế là đủ, trong khi bạn dành cả sự nghiệp mà không hề biết chỉ cách đó vài mét còn có cưa bàn, router, máy chà nhám và cả bộ nêm

    • Hầu hết framework ORM đều có chức năng thực thi raw SQL, nên dùng ORM không có nghĩa là bạn không thể dùng những tính năng đó
      ORM đúng là khiến người ta chủ yếu nghĩ theo các câu lệnh CRUD đơn giản, nhưng có lẽ như vậy lại tốt hơn và các tính năng nâng cao nên được dùng tiết kiệm
    • Tôi cảm thấy mình may mắn vì chủ yếu làm các dự án không có cơ sở dữ liệu
      Dù vậy, khi buộc phải dùng DB thì tôi cũng cố tình chỉ dùng một tập con rất nhỏ của các tính năng DB
      Vì tôi xem “code” là phần có thể kiểm soát được, còn “cơ sở dữ liệu” là một vùng nguy hiểm, khó chạm tới, nơi lỗi chỉ lộ ra về sau ở những chỗ như integration test chậm chạp
      Logic xảy ra bên trong DB nằm ngoài phạm vi của các kiểm thử mức thấp nên khiến tôi thấy đáng ngại, và có lẽ tôi sẽ không dùng cả truy vấn select-from-select
      Tôi sẵn sàng bỏ lại phần lớn nhà kho dụng cụ đó
    • Vì thế cũng không có gì đáng ngạc nhiên khi nhiều lập trình viên không nhìn ra khác biệt giữa SQL và NoSQL
    • Khi hệ thống mở rộng quy mô, việc phi chuẩn hóa cơ sở dữ liệu rất mạnh vì mục đích hiệu năng rồi dùng nó như một kho lưu bit ngu ngốc bị phóng đại là chuyện thường thấy
    • Tôi đã làm vài dự án kiểu “không cần mấy tính năng cơ sở dữ liệu hào nhoáng đó”, và xử lý hậu quả của chúng thật sự rất bực bội
      Phần lớn thời gian bị tiêu tốn để tạo ra các giải pháp vòng vo tinh vi chỉ nhằm phát minh lại những cơ chế bảo vệ toàn vẹn dữ liệu cơ bản
      Tự làm cơ sở dữ liệu của riêng mình có thể vui cho mục đích học tập, nhưng không phải cho ứng dụng production
  • ActiveRecord lúc nào cũng cho cảm giác rất ổn
    Có vài trường hợp ngoại lệ, nhưng nó đánh trúng điểm 80/20, cho phép thoát xuống SQL khi cần, và các lifecycle hook để tổ chức domain logic cũng rất tuyệt và dễ hiểu
    Tôi thích những ORM thực dụng, không giấu mọi thứ sau một “bundle” hay đưa vào một ngôn ngữ truy vấn riêng
    Những ORM cố gắng “giải quyết” hoàn toàn vấn đề ORM mới là thứ khó làm việc cùng

    • ActiveRecord thực sự rất sướng để dùng, và khi gặp edge case thì lúc nào cũng có thể quay về raw SQL
      ORM nên được làm theo cách này
    • Thật khó tưởng tượng việc xây lại một ứng dụng CRUD lớn mà không có ActiveRecord; khả năng đọc hiểu và bảo trì sẽ kém đi
      Tuy nhiên, để dùng đúng thì cần có nền tảng SQL tốt từ trước
      Vấn đề xảy ra khi mọi người không học SQL và không hiểu các truy vấn mà AR sinh ra cũng như các giới hạn của nó
    • Tôi không thực sự hiểu những lời phàn nàn về ORM, nhưng có thể là vì ORM duy nhất tôi từng dùng là ActiveRecord
  • Tiền đề đã sai. ORM chưa từng là một “ý tưởng có hại”
    Có những ORM rất kinh khủng để làm việc cùng, nhưng điều đó không có nghĩa cả thể loại này là một ý tưởng tồi
    Tôi đã dùng SQLAlchemy rất thoải mái suốt nhiều năm vì nó không cố tái phát minh chính khái niệm SQL
    Thay vào đó, nó cung cấp một tầng tiện lợi để xây dựng và truyền truy vấn, đồng thời xử lý đúng các chi tiết lặt vặt để tôi không phải tự mình vọc vào

    • Kinh nghiệm ORM của tôi gần như chỉ là với SQLAlchemy và học qua Ecto một chút, nên đọc chuỗi này lại khiến tôi tự hỏi có phải các ORM khác đều kinh khủng không
      Gần đây khi học Rust, việc tự viết mã khuôn mẫu để xử lý CRUD đơn giản khiến tôi thấy đau đớn và như một sự lãng phí thời gian
      ORM của SQLAlchemy có thể hơi cản trở với các truy vấn phức tạp hơn hoặc cần tối ưu, nhưng trong những trường hợp đó bạn luôn có thể hạ xuống viết SQL trực tiếp
    • Đã rất lâu rồi tôi mới lại thấy ai dùng đúng cụm “begging the question”
    • Tôi có thể đề xuất SQLAlchemy ở chỗ nó giúp bạn tự tin viết SQL theo cách mà mã nguồn khớp chặt với truy vấn
  • Cũng như mọi thứ khác, có những tình huống phù hợp và không phù hợp, và công nghệ rất dễ bị lạm dụng hoặc dùng sai
    Ví dụ, các ORM như Entity Framework của .NET có thể là một tài sản lớn, đặc biệt khi làm việc theo hướng database-first
    Nếu bạn có một DB với hàng trăm bảng và các quan hệ khóa ngoại đúng đắn, ORM giúp khám phá schema phức tạp và cung cấp kiểm tra ở thời điểm biên dịch khi thay đổi
    SQL nhúng là con dao hai lưỡi, trông như có lợi thế hiệu năng nhưng lại cực kỳ khó bảo trì khi SQL dài dòng bị hardcode, đặc biệt là ở các phép join
    Tôi không nói về lúc có 5~10 bảng mà là khi có hàng trăm bảng
    Tôi không định thuyết phục ai, nhưng bạn không nên tin nguyên xi những gì đọc được trên mạng về các best practice
    Mọi người có thể đang nói về những thứ hoàn toàn khác nhau, và dù dùng công nghệ nào thì vẫn luôn có lập trình viên tệ

  • Nếu không dùng ORM hay giao diện DB nào khác, cuối cùng bạn sẽ tự viết và bảo trì một cái cho những thứ như CRUD
    Và rất có thể trong đó sẽ có những bug kỳ quặc, edge case và lỗ hổng bảo mật
    Cũng có vấn đề khi các nguyên tắc như SOLID được dạy trong đại học như thể đó là các nguyên tắc thiết kế phần mềm phổ quát
    Trong học thuật thì điều đó có lý vì các ràng buộc như thời gian, khả năng bảo trì, tiền bạc và công sức ít quan trọng hơn, nhưng khi sinh viên mới ra trường bước vào môi trường thực tế thì sẽ có một cú va chạm rất tệ
    Vì họ đã quen dành cả ngày để yak shaving và tái phát minh bánh xe nhằm tìm ra lời giải “hoàn hảo”
    Nếu không có đủ senior/lead đủ năng lực để chặn việc đó, bạn sẽ có một sản phẩm rất “thú vị”, vượt ngân sách, cháy lớn, hoặc là tổ hợp của tất cả những thứ đó
    Tệ hơn nữa là khi thứ gì đó được tạo ra trong học thuật và thiết kế cho học thuật lại được tung ra công chúng
    Ví dụ điển hình vừa tốt vừa xấu là OpenStack, với vô số tay nắm và nút chỉnh để hỗ trợ môi trường đặc thù của CERN và hàng trăm trường đại học, cùng các thành phần được mô-đun hóa đến mức điên rồ, nhưng nó quá kiểu Byzantine nên gần như không thể bắt đầu được

    • Trong chuỗi này có nhiều lời phàn nàn rằng ORM không đúng như lời hứa của nhà cung cấp, và điều đó là thật
      ORM không giúp bạn đổi cơ sở dữ liệu mà không tốn công, không loại bỏ nhu cầu phải học và hiểu SQL mà nó sinh ra, cũng không làm schema trở nên mượt mà hơn
      Dù vậy người ta vẫn dùng nó vì đó là một lớp trừu tượng tiêu chuẩn mà kiểu gì bạn cũng sẽ phải sao chép lại
      Nó cho phép cache truy cập DB, thứ mà với SQL thủ công thì càng ở quy mô lớn càng khó một cách đáng ngạc nhiên
      Nó cũng đi kèm công cụ chuẩn hóa và best practice, giúp những vấn đề như N+1 trở nên không còn đáng ngại
      Vấn đề chính của ORM là kỳ vọng sai và những ORM tệ
      Nếu muốn biết giá trị của ORM, hãy mở một dự án được viết mà không có ORM rồi thử sửa bất kỳ thứ gì
    • Câu “cuối cùng bạn sẽ tự làm lấy một cái” chỉ đúng nếu bạn cứ khăng khăng tổ chức logic theo đối tượng thay vì theo “hàng”
      Xem https://news.ycombinator.com/item?id=36498583
    • Một vài bộ phận và nhiều công ty đã khổ sở hoặc thất bại vì một hoặc vài siêu thiên tài theo đuổi sự hoàn hảo
      Công việc hữu ích không tiến triển, hoặc mất lâu hơn 20~100 lần so với cách tiếp cận nhanh và bẩn
      Tệ nhất là trường hợp một đội designer HTML lành nghề bị ép phải học lập trình hàm
      Phù thủy cấu hình backend đó là một kỳ quan của kỹ thuật hiện đại và thiết kế thanh nhã, nhưng mất 10 người làm trong 1 năm
      Tiền VC khiến con người trở nên kỳ quặc
    • Tôi đã nghe quá nhiều điều tiêu cực về SQLAlchemy nên quyết định tự làm một query builder nhẹ, nhưng sau 8 tháng thì cuối cùng vẫn đang dùng SQLAlchemy
      Ngôn ngữ SQL rất phức tạp, và việc làm ra một query builder đúng nghĩa, đầy đủ tính năng không hề là chuyện nhỏ nhặt
      Điều này vẫn đúng ngay cả khi bỏ qua phần ánh xạ quan hệ thực tế
    • Dùng một ORM tốt vẫn hơn, nếu không thì bạn sẽ phải tự gánh lấy một nửa của một ORM tệ
  • Tôi cho rằng SQL là một trường hợp đúng với kiểu “cứ tự mình lập trình cái chết tiệt đó đi”
    Học SQL hay thư viện ORM thì thực ra không có nhiều ý nghĩa bằng việc tự học và tự viết SQL
    Truy cập cơ sở dữ liệu có lẽ là công việc nền tảng bạn sẽ làm suốt cả sự nghiệp, nên tốt nhất là học trực tiếp kỹ năng thực tế đó
    CSS cũng tương tự: thay vì học thư viện CSS thì nên học và dùng CSS
    Thực ra gần như mọi mặt của phát triển frontend cũng vậy, thay vì “thư viện form” thì cứ làm việc trực tiếp với form API của trình duyệt
    Cá nhân tôi thấy SQL dễ học hơn nhiều so với bất kỳ ORM nào
    Đừng lãng phí chi phí để học những thứ sai hướng

    • Nếu học SQL trước, bạn cũng sẽ biết khi nào nên dùng ORM và có thể đánh giá ORM tốt hơn
      CSS cũng vậy, nếu biết CSS thì khi chọn thư viện cho tiện lợi bạn sẽ linh hoạt hơn nhiều và hiểu rõ hơn
    • SQL vẫn được gọi là ngôn ngữ vĩnh cửu
      Qua nhiều nền tảng phát triển như mainframe, desktop, client/server, web và các ngôn ngữ khác nhau trên đó, SQL luôn là sợi chỉ chung xuyên suốt
      Tôi hoàn toàn đồng ý rằng lập trình viên hiểu rõ tầng dữ liệu và những gì diễn ra bên dưới sẽ là lập trình viên mạnh hơn
      Nếu đã định dành thời gian để học những điểm kỳ quặc của ORM, thì câu hỏi kinh điển là: tại sao không đầu tư thời gian đó vào việc học SQL?
      Lợi ích cộng dồn của kinh nghiệm SQL sau 10–15 năm rất có thể sẽ giá trị hơn ORM của một nền tảng hay ngôn ngữ cụ thể
      Có lý do để học và dùng ORM, nhưng không nên nghiêng về ORM chỉ vì không muốn học SQL
    • Cốt lõi gần hơn với việc “hãy học nền tảng trước khi học trừu tượng hóa
      Có như vậy bạn mới biết mình phải trả giá gì cho lớp trừu tượng đó. Tất nhiên, nếu là “trừu tượng hóa không chi phí” thì khác
    • Từ năm 1992 tôi đã trực tiếp viết SQL bằng Pro*Pascal, CS130
      Nhưng giờ tôi đang dùng crud của azer, thứ có thể xếp vào ORM, vì việc marshal/unmarshal với kiểu Go
      sql trong thư viện chuẩn Go quá cơ bản, nên gần như phải tự viết phần marshal/unmarshal đối tượng, rất tẻ nhạt
      Nếu ngày nay dùng ORM thì có lẽ tôi chỉ dùng đúng cho mục đích đó
  • Tôi thích các micro ORM ở “điểm giữa” như Dapper hay Diesel
    Bạn viết truy vấn bằng SQL nhưng vẫn có kiểu mạnh và tự động ánh xạ tập kết quả sang đối tượng
    Tức là vừa có năng suất vừa có một ngôn ngữ truy vấn “thực thụ” đầy đủ tính năng
    Tôi cũng dần thích cách chỉ dùng object mapper cho các truy vấn chỉ đọc
    Cập nhật thì thực hiện bằng command pattern, tức stored procedure, và có thể ánh xạ để chúng trông như các phương thức native

    • Dapper là giải pháp truy cập dữ liệu thú vị nhất mà tôi từng dùng
      Lập trình viên nên chấp nhận sự tồn tại của SQL thay vì giả vờ như nó không có
    • Tôi thích việc không phải tự viết các hàm model cuối cùng chỉ để ánh xạ sang SQL, hoặc viết đống mã PHP khuôn mẫu để thực hiện truy vấn có tham số cho đúng cách
      Eloquent trừu tượng hóa đủ tốt mấy việc lặt vặt đó để tôi tập trung vào logic
      Tôi đang viết lại một ứng dụng PHP “kiểu cũ” chỉ có mảng và truy vấn sang cấu trúc dùng model và Eloquent, và cảm giác như thay đổi hoàn toàn cuộc chơi
    • Tôi thích điểm ở giữa. Trong TypeScript chẳng hạn có Kysely hoặc dự án của tôi là Zapatos
    • Tôi nghĩ đây là điểm tối ưu
      Trước đây khi dùng SQLite trên .NET tôi cũng từng làm một thư viện wrapper theo hướng đó: https://github.com/zmj/sqlite-fast
  • Chủ đề này cho thấy rất rõ sự thống trị về mindshare của ORM
    Trong gần như mọi bình luận, phương án thay thế ORM được ngầm hiểu là raw SQL dưới dạng chuỗi literal trong mã ứng dụng
    Từ 6 năm trước đã có các công cụ dùng SQL như một ngôn ngữ riêng và sinh wrapper để phơi bày truy vấn thành phương thức trong ứng dụng
    Ví dụ như queryfirst, pgtyped, pugsql, sqlc
    Đây là một sự chuyển đổi mô hình, hoặc ít nhất đã có thể là như vậy, và rõ ràng cho thấy đó là cách làm vượt trội hơn, nhưng ORM và cuộc tranh luận bất biến quanh nó đã hút hết không khí, khiến hướng này vẫn ở bên lề

    • Đúng vậy. Go cũng có sqlc
      SQL là một ngôn ngữ, mà còn là ngôn ngữ ở mức rất cao nữa
      Cố gắng chắp vá SQL từ một ngôn ngữ cấp thấp hơn là ví dụ điển hình của kiểu “cầm búa thì mọi thứ đều trông như cái đinh”
      Ví dụ, nếu bạn đang code bằng assembly cấp thấp và phải tương tác với một hệ thống lớn, mà tài liệu của hệ thống đó nói đầu vào chỉ có thể biểu diễn bằng JavaScript, thì bạn có lựa chọn
      Bạn có thể mang về một thư viện khó hiểu để chắp vá mọi thứ bằng assembly thuần và khỏi phải commit file xxx.js vào kho mã, hoặc đơn giản là dùng sức mạnh của ngôn ngữ cấp cao bằng cách biểu diễn nó bằng file .js
    • Sau khi dùng sqlc trong một dự án, tôi nghĩ mình không thể quay lại nữa
      Bạn có được một nguồn chân lý duy nhất cho các truy vấn SQL đang dùng, và vì thứ thực sự được viết và bảo trì là chính các truy vấn đó, nên việc tối ưu như tạo chỉ mục mới cũng trở nên dễ dàng
    • ActiveRecord thì lúc nào cũng dở, nhưng arel dùng để cấu thành truy vấn bên trong nó thì tôi luôn thích
      Đặt một lớp trừu tượng lên trên chuỗi raw SQL là rất hữu ích, nhưng giả vờ quan hệ là đối tượng thì là một cách làm phức tạp và rối rắm
    • Enterprise Objects Framework của NeXT đã hỗ trợ điều này từ hơn 25 năm trước
      Có lẽ TopLink cũng vậy
      Đó là cách nó xử lý việc ánh xạ giữa stored procedure và local entity
    • Tôi tò mò không biết đó là những công cụ nào