This sample demonstrates how to handle complex tree operations with simple logic. We define two entities: Project and Task. Projects contain Tasks, which, in turn, can contain other Tasks (thus forming a tree).
We have a created a simple web application to demonstrate this. It looks like this:
This application is available as a war file, which can be deployed to most modern containers (Tomcat, JBoss, Jetty, etc...), or as a Maven project. To run the Maven project, you can just unzip it, open a command line in the newly created directory, and execute
In both cases, once the application is running, you can connect to it from your browser at http://localhost:8080/BusLogicSamplesTask
The application allows you to create, update and delete nodes in the tree (there is a right-click menu), including moving nodes around.
The data model behind this is fairly basic:
Our requirements for this project are (seemingly) simple:
Now let's think about what it would take to code the business logic using normal coding techniques (regardless of language). Requirement 3 is of course the killer, because it means that requirements 1 and 2 have to be fulfilled no matter how Tasks are moved around. It's not that it's impossible, or even terribly difficult, but it clearly would take some thinking to get this right, a fair amount of code to implement, and probably quite a bit of debugging to get it right in all cases.
Fortunately, we're using declarative logic.
Requirement 1 is fairly simple: we define Project's
However, the requirements specify that this should be the sum of all subtasks, recursively, and not just the top-level tasks. We therefore need to roll up the subtasks' hours. We do this by defining three attributes in Task:
With this set up, we can then define two simple annotations to implement this logic for Task:
These three annotations takes care of requirement 1: the Project will now contain the total number of hours of all its subtasks, recursively.
Requirement 2 seems more complicated. We must find a way for each Task to somehow know its position in the tree, so it can determine its name. The easiest way to do this is to define a relationship among Tasks at the same level, effectively making them a linked list, e.g.:
We're also going to define two attributes for Task:
With this in place, this requirement can be satisfied with two simple logic methods for Task:
Let's look at this in a little more detail.
That takes care of requirement 2. Now on to the most difficult one...
Requirement 3 should cause a bit of wincing among all programmers. If a Task is moved, let's say from one Project to a different Project, there is quite a lot of work to do:
Here's where declarative programming really shines: we're done! By declaring the five bits of logic above, we have taken care of the business logic for all use cases. We are literally done, because the logic will be automatically enforced by ABL, for all use cases.
I hope it's clear from this example that declarative logic can be a tremendous time-saver for a whole class of problems.