qhwa
Hi all!
I’m excited to share with you my recent work on making business rules easier to maintain.
Tablex is an implementation of Decision Table in Elixir. A decision table can make rules more obvious to human brains. Hence we can easily spot the mistakes in rules. Since they are simple markups, Elixir developers can transfer the responsibility of maintaining the domain business rules to operation teammates.
Tablex is heavily inspired by Decision Model and Notation (DMN) and borrowed its ideas of decision tables and friendly expression language. While DMN uses XML to present the decision table, Tablex uses easier-to-read plain texts, such as:
F score || result
1 >=90 || A
2 60..89 || B
3 - || C
What is it related to Elixir?
As a library, Tablex has the following abilities:
- Parsing the above text into a rich Elixir struct,
%Tablex.Table{...}. - Compiling the above text into corresponding Elixir code that uses pattern matching. (doc)
- Allowing employing functions in the output definition to be your domain rule engine. (doc)
- Allowing displaying decision tables in HTML pages with the help of TablexView
In a word, it allows you to work with your business rules conveniently.
Let’s see a more real-life example:
The following Elixir code defines what settings for a given place (identified by city and area id) are:
default_settings = %{
feature1: [
param1: 5,
param2: 0
],
feature2: [
param1: 0.6,
param2: 60
],
feature3: [
feature3_1: [
param1: 0.75,
param2: ["SOMETHING"]
],
feature3_2: [
param1: 0.75,
param2: ["SOMETHING"]
]
],
feature4: false,
feature5: false,
feature6: [
feature6_1: true,
feature6_2: true,
feature6_3: false
],
feature7: "provider1",
feature8: false
}
case target do
%{city: "Vancouver"} ->
%{default_settings | feature8: true}
%{area_id: 2053} ->
default_settings
|> put_in([:feature6, :feature6_3], true)
|> put_in([:feature8], true)
|> put_in([:feature3, :feature3_1, :param2], ["SOMETHING", "ANOTHER"])
|> put_in([:feature3, :feature3_2, :param2], ["SOMETHING", "ANOTHER"])
%{area_id: area_id} when area_id in [7839, 2407, 21094, 62124] ->
default_settings
|> put_in([:feature4], true)
|> put_in([:feature8], true)
%{area_id: area_id} when area_id in [23982, 2407, 2462, 2179] ->
%{default_settings | feature7: "provider2"}
%{area_id: area_id} when area_id in [2060, 2413, 2407, 2417, 2396, 2419] ->
default_settings
|> put_in([:feature8], true)
|> put_in([:feature6, :feature6_3], true)
_ ->
default_settings
end
With Decision Table, we can describe almost the same logic in a tabular text block:
====
R || 1 2 3 4 5 6
target.city || - Vancouver - - -
target.area_id || - - 2053 7389,2407,21094,62124 23982,2407,2462,2179 2060,2413,2407,2417,2396,2419
====
feature1.param1 || 5 - - - - -
feature1.param2 || 0 - - - - -
feature2.param1 || 0.6 - - - - -
feature2.param2 || 60 - - - - -
feature3.feature3_1.param1 || 0.75 - SOMETHING,ANOTHER - - -
feature3.feature3_1.param2 || [SOMETHING] - - - - -
feature3.feature3_2.param1 || 0.75 - - - - -
feature3.feature3_2.param2 || [SOMETHING] - SOMETHING,ANOTHER - - -
feature4 || Y - - Y - -
feature5 || N Y - - - -
feature6.feature6_1 || Y - - - - -
feature6.feature6_2 || Y - - - - -
feature6.feature6_3 || N - Y - - Y
feature7 || provider1 - - - provider2 -
feature8 || N - Y Y - Y
I said “almost the same” because the settings differ between the Elixir code and the table when the area id is 2407. The maintainer of the code intended to set multiple settings for area #2407 with different clauses of pattern matching. But he didn’t know that only the first matched clause was hit. This mistake was quickly spotted when the rules were present on a table.
Also, here we use R, the reverse merge hit policy so that settings will be merged with all hit rules from right to left. So rules #6, #5, #4, and #1 will all impact the final result.
The textural table is easy to read and can be further visualized with HTML:
(generated by TablexView)
Roadmap
1.0
- formatter
- expression specification
1.1
- more data types (Date, Time, and DateTime)
Current status
It’s still a very early stage for Tablex, although it has been used in production. Currently, the latest version is 0.3.1.
I hope you can find this helpful to you. Feedback is welcome! Thanks!
Trending in Announcing
Other Trending Topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #library
- #deployment
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #podcasts
- #javascript
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #ai
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #blog-post
- #elixirconf-us
- #elixir-ls
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #elixirconf-eu
- #api
- #forms
- #security
- #metaprogramming











Showing Posts 1 to 3- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
qhwa
New version released today: v0.2.0-alpha.1, with many exciting changes:
I’m very excited about what is ahead. Feedback is very welcome!
qhwa
New version released: v0.2.0. Compared to v0.2.0-alpha.1, there are many changes:
qhwa
New version released: v0.3.1, with some improvements and bugfixes:
myFoowill stay asmyFoo. In contrast, previous versions will convert it tomy_foo.