Trong sprint cuối tháng 5 vừa rồi, team mình được giao nhiệm vụ thiết kế và triển khai một module về “Rule engine” hoàn toàn mới cho hệ thống. Sau khi thực hiện sprint planning thì mình là người có nhiệm vụ thiết kế cơ sở dữ liệu và phụ trách cho các API để lưu các “rules” này vào hệ thống. Mình đã có một kinh nghiệm thú vị cho việc này.
Rule engine
Đầu tiên, về “rule engine”, đây là một module cho phép người dùng đặt ra một số các điều kiện cụ thể nào đó cho một thao tác bất kì. Do đây không phải là một thứ gì quá mới và bí mật (vì bên ngoài cũng đã có nhiều rule-engine khác như là: json-rule-engine, …), mình sẽ lấy 1 ví dụ cụ thể cho dễ hình dung:
Rule 1: Nếu [Transaction đơn lẻ] lớn hơn 500$, thì giao dịch đó [Cần sự đồng ý] của ít nhất [1 Manager].
Hoặc Rule 2: Nếu [Thu nhập trung bình theo năm] của một người [Ít hơn] [20.000$], thì [Không cho phép] họ mua bán [Sản phẩm X]
Một cách bình dân thì rule engine sẽ là các bộ lọc điều kiện và đưa ra hành động tương ứng. Có các hệ thống thuộc thể loại “hệ chuyên gia” (Expert system) cũng có chức năng gần tương tự nhưng phức tạp hơn, nhưng mình không bàn nó ở đây.
Thiết kế
Ban đầu khi nhận được yêu cầu, mình dành trọn 1 buổi sáng để ngẫm nghĩ về thiết kế CSDL cũng như các module liên quan, với mong muốn là đáp ứng được nhiều nhất và đa dạng nhất các “rules”. Và đây là thiết kế ban đầu, một thiết kế mà mình nghĩ là có khả năng bao quát nhiều trường hợp nhất có thể:

Mình đem thiết kế này ra cho nhóm và trình bày về nó, mọi thứ có vẻ ổn ngoại trừ một chi tiết: Chi phí để triển khai theo thiết kế này là quá rủi ro về mặt thời gian. Ngoài ra, chưa chắc gì hệ thống đã cần tới những “trường hợp phức tạp”, kể cả Project Manager cũng không chắc là hệ thống sẽ cần, người dùng cuối mới là người quyết định.
Mình nhận ra một điều, ở một sản phẩm trong giai đoạn startup, điều chúng ta cần là một hệ thống có thể hoạt động sớm nhất có thể, ít nhất là đáp ứng được các tình huống cơ bản. Nếu phản hồi của khách hàng tích cực, các tính năng “nâng cao” mới có cơ hội được cân nhắc thêm vào sau đó. Một startup có thể sẽ không đủ nguồn lực để làm mọi thứ tốt ngay từ đầu. Hay nói cách khác: hoàn hảo ngay từ đầu là điều không cần thiết.
Sau khi cân nhắc, bản thiết kế cho CSDL được điều chỉnh lại, một sự giản lược đáng kinh ngạc:

Đánh đổi lại cho thiết kế này là việc Admin sẽ không có tính năng thêm vào hay bớt đi tập các điều kiện và hành động, mà các thứ đó được cố định bởi các CONSTANT trong source code. Một sự đánh đổi cho Admin, nhưng về mặt người dùng thì vẫn đủ. Tức là: người dùng vẫn sẽ có bộ các rule mà họ muốn, họ sẽ không nhận ra sự khác biệt về mặt tính năng. Quyết định được đưa ra: đánh đổi sự “hoàn thiện của thiết kế kĩ thuật” lấy sự “hợp lý trong thời gian phát hành sản phẩm tới người dùng”.
Nếu
Nếu mình đưa bản thiết kế cuối cùng cho các bạn “dân kĩ thuật” khác đánh giá, có lẽ nó không được đánh giá cao, nhưng đứng ở góc cạnh người sản phẩm, tính năng đã được đưa ra đúng hạn, người dùng đã có được thứ mà họ muốn. Nếu coi người dùng là trung tâm của sự phục vụ, chúng ta có thể nói rằng: bản thiết kế này hợp lý, kể cả khi nó không phải là thiết kế tốt nhất.

Về bài toán mở rộng trong tương lai, sự “refactor” là không thể tránh khỏi, nhưng … Thiết kế hiện tại cho phép bạn thực hiện chuyển đổi dữ liệu sang format của thiết kế hoàn chỉnh hơn ở phiên bản đầu tiên (hoặc phiên bản nào đó hoàn hảo hơn). Việc cho phép chuyển đổi này là điều quan trọng. Ngoài ra chúng ta nên có thêm lớp adapter ở trung gian, để che giấu cái cụ thể với lớp abstraction phía trên.

Ở một khía cạnh nào đó, phần mềm có nhiều nét tương đồng với cuộc sống, rất khó để đánh giá một điều gì đó là tốt hay xấu. Để đơn giản, chúng ta thường chọn lấy “thứ phù hợp” với hoàn cảnh. Không có gì đúng hoàn toàn, hoặc sai hoàn toàn, chỉ có thứ hợp lý mà thôi.