Thực thi business logics trong Go với Grule Rule Engine

Trong bài viết này, chúng ta sẽ khám phá lợi ích của rule engine trong việc xử lý các business logics, đặc biệt tìm hiểu về Grule - một rule engine được viết bằng ngôn ngữ Go.

Đối với những bài toán không nặng về xử lý nghiệp vụ, các business rules được thực thi bằng cách sử dụng các vòng lặp và điều kiện lồng ghép với nhau. Tuy nhiên, đối với những bài toán nặng về xử lý nghiệp vụ như hệ thống bán hàng, ngân hàng hoặc bảo hiểm, business logics trở nên phức tạp và thay đổi thường xuyên song song với tải dữ liệu tăng cao đòi hỏi phải tối ưu thời gian thực thi. Để giải quyết vấn đề đó, rule engine được tạo ra nhằm quản lý và thực thi các business rules một cách nhanh chóng.

Upload image

Rule Engine là gì?

Hiểu một cách đơn giản, rule engine là hệ thống chứa một danh sách các rule, mỗi rule là một mệnh đề if-then với “if” định nghĩa phần điều kiện để rule được thực thi và “then” là phần được thực thi khi điều kiện của rule thoả mãn. Khi một fact (dữ liệu cần để tính toán) được thực thi, rule engine sẽ kiểm tra fact với từng rule, thực thi những rule thoả mãn và lặp lại tiến trình trên cho tới khi không còn điều kiện nào thoả mãn.

Ưu điểm của rule engine

  • Tách biệt business logics với code logics, giúp việc xem xét và đánh giá business logics trở nên dễ dàng hơn.
  • Đơn giản, linh động: mỗi một rule là độc lập với nhau, không còn các vòng lệnh phức tạp và có thể dễ dàng cập nhật rule khi có thay đổi mới.  
  • Dễ hiểu: việc đọc hiểu các rule cũng không cần phải có kiến thức chuyên môn, giúp giảm bớt khoảng cách giữa người phân tích nghiệp vụ và đội ngũ lập trình.

Hạn chế của rule engine

Hạn chế duy nhất của rule engine là để có thể định nghĩa bộ quy tắc phù hợp đòi hỏi người sử dụng phải nắm được cách vận hành của rule engine.

Hiện nay, có rất nhiều thư viện rule engine để chúng ta lựa chọn tuỳ thuộc vào nhu cầu và ngôn ngữ lập trình mà ta sử dụng như Drools dành cho Java, Rules Engine cho C# hay Grule dành cho Golang. Sau đây, chúng ta hãy cùng tìm hiểu về cách thức hoạt động cũng như cách vận hành của Grule nhé.

Tìm hiểu về Grule

Grule là một bộ rule engine được viết cho ngôn ngữ Go, được lấy ý tưởng từ Drools nhưng cơ chế vận hành của Grule đơn giản và dễ hiểu hơn nhiều. Đầu tiên, hãy cùng xem qua một số định nghĩa để có thể bắt đầu tìm hiểu về cách thức hoạt động của Grule.

  • Fact: là dữ liệu cần được tính toán, thường tồn tại dưới dạng object chứa các thông tin của một chủ thể nhất định.
  • Rule: là quy tắc mà fact phải kiểm tra trong quá trình thực thi, bao gồm điều kiện và hành động khi điều kiện được thoả mãn. Để tránh trường hợp phát sinh lỗi khi nhiều rules được kích hoạt cùng một lúc, mỗi rule sẽ được định nghĩa một mức độ ưu tiên salience để rule engine chọn ra rule để ưu tiên thực thi.
  • Knowledge Base: là tập hợp các rule được rule engine sử dụng trong quá trình thực thi.
  • Conflict Set: là tập hợp các rule thoả mãn điều kiện khi một fact được kiểm tra qua.

Khi bắt đầu thực thi fact sẽ được kiểm tra qua loạt rule trong knowledge base, những rule nào thoã mãn điều kiện sẽ được đưa vào trong conflict set. Sau đó, rule engine sẽ chọn lấy một rule trong conflict set để thực thi. Trong trường hợp có nhiều rule trong conflict set, rule engine sẽ dựa vào mức độ ưu tiên của rule để quyết định rule nào sẽ được chọn. 

Quá trình trên được gọi là một cycle, và rule engine sẽ lặp lại cycle cho đến số lượng vòng lặp vượt quá ngưỡng hoặc không còn rule nào trong conflict set. Toàn bộ tiến trình trên có thể được tóm gọn trong sơ đồ dưới đây:

Upload image

Cách vận hành trên tuy đơn giản nhưng đã đủ để có thể đáp ứng hầu hết các yêu cầu nghiệp vụ trong các dự án.

Các ưu điểm của Grule

Dễ dàng định nghĩa rule

Grule hỗ trợ hai kiểu định nghĩa rule là Grule Rule Language (GRL) và JSON để chúng ta có thể lựa chọn tuỳ thuộc vào mục đích sử dụng.

GRL là một DSL (domain specific language) để định nghĩa rule. Dưới đây là cấu trúc của một rule viết dưới dạng GRL:

rule <RuleName> <RuleDescription> [salience <priority>] {
    when
        <boolean expression>
    then
        <assignment or operation expression>
}

Có thể thấy, với GRL, việc đọc hiểu một rule là cực kì đơn giản với các từ khoá dễ hiểu, giúp đội dự án có thể nắm và kiểm soát được business logics đang được áp dụng. 

Tuy nhiên, nếu dự án yêu cầu phải hỗ trợ cấu hình hoặc chỉnh sửa rule trên giao diện cho người dùng, thì JSON là một lựa chọn đáng cân nhắc hơn bởi tính linh động và có cấu trúc dễ đọc bằng ngôn ngữ lập trình.

Ví dụ JSON format:

{
    "name": "SpeedUp",
    "desc": "When testcar is speeding up we keep increase the speed.",
    "salience": 10,
    "when": ...,
    "then": [
        ...
    ]
}

Cung cấp sẵn nhiều cách để lưu trữ rule

Grule hỗ trợ đọc rule từ bất cứ đâu, từ dưới dạng text trong code hay file và git đều được, giúp chúng ta có thể dễ dàng lưu trữ rule và cập nhật chúng mà không cần phải deploy lại hệ thống.

Dễ dàng xử lý fact object

Grule nhận vào fact object theo dạng con trỏ nên có thể dễ dàng truy cập các thuộc tính được export của fact. Điều lợi hại nữa là chúng ta hoàn toàn có thể định nghĩa các hàm custom trong fact để có thể giấu các thao tác xử lý logic bên trong fact nhằm hạn chế tối đa sự tồn tại của code logic bên trong rule.

Ngoài ra, Grule cũng cung cấp rất nhiều hàm hàm utils có sẵn để ta có thể sử dụng như các hàm xử lý thời gian, xử lý chuỗi, kiểm tra rỗng… Có hai hàm rất lợi hại trong quá trình xử lý mà chúng ta nên biết là hàm Retract để loại rule ra khỏi các cycle sau hay hàm Changed để báo cho rule engine tính lại cache.

Các bạn có thể tham khảo thêm về các hàm trong document của Grule.

Tối ưu hoá hiệu năng nhờ thuật toán RETE

Với bản chất phải chạy nhiều vòng lặp để kiểm tra qua các rules, phần điều kiện của rule được gọi lại nhiều lần, hiệu năng của rule engine có thể bị chậm đi rất nhiều nếu trong phần điều kiện của rule có gọi đến một hàm nặng về xử lý.

Để cải thiện hiệu năng của rule engine trong quá trình chạy, Grule đã áp dụng Rete – một thuật toán matching, để có thể thực hiện cơ chế caching nhằm đảm bảo mỗi hàm chỉ được gọi một lần và chỉ được gọi lại khi tham số của chúng thay đổi.

Tuy nhiên, đối với các tham số được cập nhật thông qua các hàm custom được định nghĩa thêm trong fact, rule engine sẽ không thể nhận biết được những thay đổi và sẽ tiếp tục sử dụng kết quả đã được cache trước đó. Để tránh trường hợp này xảy ra, chúng ta có thể sử dụng hàm Changed như đã nói ở phần trên để Grule có thể thực hiện tính toán lại.

Tham khảo thêm thư viện của Grule tại đây.

Atekco - Home for Authentic Technical Consultants