React-Awesome-Query-Builder là một thư viện mã nguồn mở cho phép xây dựng giao diện truy vấn trực quan trong ứng dụng React. Thiết kế kiến trúc của nó tập trung vào tính mô-đun, khả năng mở rộng và tách biệt rõ ràng giữa logic nghiệp vụ và giao diện người dùng.
Kiến trúc đa gói (Monorepo)
Dự án được tổ chức theo mô hình monorepo với các gói con nằm trong thư mục packages/:
- core: Xử lý logic truy vấn, chuyển đổi dữ liệu và quản lý cấu hình
- ui: Cung cấp các thành phần giao diện cơ bản
- ui/widgets: Triển khai các widget tương tác như dropdown, input, toggle
- các theme: Bao gồm
antd,material,bootstrapđể tùy chỉnh giao diện
Sự phân tách này giúp mỗi lớp đảm nhiệm một trách nhiệm duy nhất, dễ kiểm thử và bảo trì.
Xử lý dữ liệu và quản lý trạng thái
Lớp core định nghĩa cấu trúc cây (query tree) để biểu diễn điều kiện truy vấn. Mỗi nút trong cây có thể là:
- Group: Nhóm các điều kiện con với toán tử AND/OR
- Rule: Một điều kiện đơn lẻ gồm trường, toán tử và giá trị
Trạng thái cây được lưu trữ và cập nhật thông qua TreeStore, sử dụng immutable data structure để đảm bảo tính toàn vẹn khi thay đổi.
// Ví dụ về cấu trúc cây truy vấn
{
id: 'g1',
type: 'group',
children: [
{
id: 'r1',
type: 'rule',
field: 'age',
operator: 'greater_than',
value: 18
}
],
conjunction: 'AND'
}
Giao tiếp giữa các thành phần
Luồng dữ liệu tuân theo nguyên tắc một chiều:
- Người dùng tương tác với UI (thêm điều kiện, đổi toán tử...)
- Gọi hàm từ đối tượng
actions(được inject qua props) TreeStoretạo bản sao mới của cây với thay đổi tương ứng- Component gốc nhận lại cây mới qua props và re-render đệ quy
Component Builder đóng vai trò gốc, nhận vào tree, config và actions, sau đó render đệ quy các Item dựa trên loại nút:
const QueryItem = ({ node, path, config, actions }) => {
if (node.type === 'group') {
return <GroupNode node={node} path={path} config={config} actions={actions} />;
}
return <RuleNode node={node} path={path} config={config} actions={actions} />;
};
Tùy biến giao diện qua hệ thống theme
Mỗi theme (ví dụ antd) cung cấp triển khai riêng cho các widget như:
FieldSelect: Chọn trường dữ liệuOperatorSelect: Chọn toán tử so sánhValueEditor: Nhập giá trị (text, number, date...)
Việc thay theme chỉ cần import đúng bộ UI và truyền vào cấu hình, không ảnh hưởng đến logic core.
Nguyên tắc thiết kế đáng học hỏi
- Tách biệt rõ ràng: Core không phụ thuộc UI, UI không chứa logic nghiệp vụ
- Cấu hình linh hoạt: Hành vi component được điều khiển hoàn toàn qua đối tượng config
- Immutable state: Mọi thay đổi tạo ra bản sao mới, tránh side effect
- Extensible API: Dễ dàng mở rộng toán tử, kiểu dữ liệu hoặc widget mới