Drools

Drools es un motor de reglas Java que se encarga de aplicar reglas de negocio dentro de nuestras aplicaciones. De esta manera aparece el concepto de Sistema de Administración de Reglas de Negocio (BRMS - Business Rules Management System), el cual nos permite parametrizar las reglas del negocio de forma externa a la aplicación que las utiliza.

En este artículo vamos a ver un poco más sobre Drools, y daremos los primeros pasos con este framework junto a un pequeño ejemplo de uso.

Generalidades

Drools es un sistema de administración de reglas de negocio (BRMS) con un motor de reglas basado en una adaptación orientada a objetos del algoritmo Rete. Permite expresar de una forma más natural las reglas de negocio interactuando con los objetos de negocio. Provee separación de lógica (reglas) y datos (hechos).
También provee soporte para la programación declarativa, y es lo suficientemente flexible para expresar la semántica del problema con un lenguaje específico de dominio (DSL).
Drools cuenta con la implementación completa de la JSR-94 Rule Engine API.
También existe un plug-in de Eclipse para facilitar el desarrollo con esta herramienta.
Para especificar las reglas Drools utiliza el lenguaje de reglas de drools (DRL) para especificar las condiciones, acciones y funciones de las mismas, las cuales se puede expresar con distintos lenguajes, como Java y MVEL. Entonces las reglas son guardadas en archivos de texto con la extensión drl.
Entre las opciones para definir las reglas aparte del DRL se pueden especificar lenguajes específicos de dominio (dsl), los cuales se asocian a un drl, y también existe la opción de especificar las reglas en una planilla de cálculo, como Excel.

Para que puede servir

Muchas veces los usuarios administran sus reglas y datos de negocio en planillas Excel que ellos manejan a la perfección. ¿A quien no le ha pasado tener que hacer cargas de datos de los sistemas que desarrollaron o mantienen y para esto le pasan una planilla de cálculo que es con la que el usuario se maneja?
Una de las cosas que se pueden llegar a hacer con Drools es dejar que el usuario siga usando sus planillas de datos para manejar su negocio, y que esta misma alimente al sistema, sin tener que desarrollar ABMs que el usuario debe aprender.
Esta es una posibilidad de uso y una parte de lo que Drools puede hacer, pero a mi modo de ver no es la esencia ni la potencia de Drools. Drools no es una alternativa a las bases de datos y los ABMs (no se creó para eso).
Lo que podemos hacer con Drools es definir un DSL que en el lenguaje coloquial del usuario defina el comportamiento de su sistema de acuerdo al negocio, y que este pueda modificar el mismo, y por ende su sistema de acuerdo a las necesidades de su negocio, sin demasiada intervención del departamento de desarrollo ni teniendo que lidiar con abstracciones e interfaces que le hacemos.
También provee una forma de acceder a los objetos de nuestro sistema y por ende modificar comportamiento en tiempo de ejecución, sin tener que compilar o desplegar la aplicación, pudiendo, por ejemplo hacer una especie de scripting o proveyendo una forma de hacer ciertas user exits para determinadas partes de nuestro sistema.
Y finalmente podemos llegar a implementar toda la lógica de negocio de nuestra aplicación.
Se utiliza también en otros productos, como en el mundo de los ESB, por ejemplo en Apache ServiceMix, para permitir configurar las reglas de enrutamiento de mensajes.
Claro que no todo se puede o se debe resolver con Drools y debemos modificar un poco la forma que pensamos las aplicaciones para que se ajusten a su paradigma.
Drools se puede utilizar como una parte de nuestro sistema, dónde se pueden dejar configurar por Drools las reglas de negocio que por ejemplo son muy cambiantes.

Ejemplo de uso

Vamos a ver un ejemplo muy simple del uso de Drools, centrándonos en las características de este y el uso del motor de reglas, y para que sea didáctico el ejemplo no nos detendremos en detalles de diseño o arquitectura, los cuáles no van a ser los más adecuados para un desarrollo profesional.
Empezaremos con un pequeño ejemplo que a través de sucesivas notas iremos incrementando para mostrar distintas características de Drools.
Supongamos que el departamento de Recursos Humanos quiere informatizar su política de ascensos, compensaciones, beneficios, etc. Lo que sabemos son ciertos parámetros de los empleados que tienen en cuenta para realizar esto, y las acciones que se toman, es decir que es lo que deciden y hacen.
Pero también sabemos que éstas políticas son muy cambiantes de acuerdo a las vicisitudes de la empresa, los proyectos, el mercado laboral, el país, los distintos jefes y las propias personas.
Para que puedan ir modificando éstas reglas según su necesidad vamos a externalizarlas del sistema por medio de Drools, así tratamos de minimizar los pedidos de desarrollo.
En ésta primera entrega vamos a dar la posibilidad que se decida el cargo y el salario de un empleado debido a un promedio de sus conocimientos técnicos (ok, es solo un ejemplo).
Para esto contamos con una clase Empleado que es la siguiente.
Empleado.java:
 
package com.dosideas.drools.ejemplo1.rrhh;
 
import java.math.BigDecimal;
 
public class Empleado {
 
    private String nombre;
    private Integer promedioConocimientos;
    private String cargo;
    private BigDecimal salario;
 
    public Empleado(String nombre, Integer promedioConocimientos) {
        this.nombre = nombre;
        this.promedioConocimientos = promedioConocimientos;
    }
 
    @Override
    public String toString() {
        return nombre + " promedio=" + promedioConocimientos + 
               " cargo=" + cargo + " salario=" + salario;
    }
 
//getters y setters...
 
}
 
 
Hasta acá todo normal, sólo hemos sobrecargado el métdo toString() para luego mostrar lo que hace Drools en nuestro ejemplo.
Los objetos con los que trabaja Drools, los que se usan de datos las reglas para decidir si aplican se llaman hechos (facts), y deben ser beans (es decir tener getters y setters).

Las reglas

Vamos a trabajar con objetos de la clase Empleado, y para esto especificaremos nuestras reglas en un archivo de lenguaje de reglas de Drools (drl).
En ésta primera aproximación a Drools, para que sea más cercano a nuestro lenguaje de programadores, sólo trabajaremos con un archivo de reglas, sin crear un DSL ni una hoja de cálculo, y en el archivo de reglas especificaremos éstas en el dialecto java, que es el que toma por defecto.
Drools evalúa la aplicación de las reglas que definamos sobre los hechos que tenga en su memoria de trabajo. Para esto, luego, en nuestra aplicación java crearemos la memoria de trabajo de Drools, insertaremos algunos empleados y le haremos aplicar las reglas que ahora vamos a definir.
PoliticaRrhh_Ej1.drl:
 
package com.dosideas.drools.ejemplo1.rrhh;
 
import com.dosideas.drools.ejemplo1.rrhh.Empleado;
import java.math.BigDecimal;
 
rule "Programador"
    when
        empleado : Empleado(promedioConocimientos >= 8, promedioConocimientos <= 10)
    then
        System.out.println("Programador: " + empleado.getNombre());
        empleado.setCargo("Programador");
        empleado.setSalario(BigDecimal.valueOf(1000));
end
 
rule "Lider de Proyecto"
    when
        empleado : Empleado(promedioConocimientos >= 4, promedioConocimientos <= 7)
    then
        System.out.println("Lider de Proyecto: " + empleado.getNombre());
        empleado.setCargo("Lider de Proyecto");
        empleado.setSalario(BigDecimal.valueOf(2000));
end 
 
rule "Gerente"
    when
        empleado : Empleado(promedioConocimientos >= 0, promedioConocimientos <= 3)
    then
        System.out.println("Gerente: " + empleado.getNombre());
        empleado.setCargo("Gerente");
        empleado.setSalario(BigDecimal.valueOf(3000));
end
 
Como vemos, en principio ubicamos las reglas en un paquete.
Luego necesitamos importar las clases java sobre las que operará el archivo de reglas.
Con rule definimos el nombre de la regla. Como ven definimos 3 reglas aquí.
Con la palabra reservada when se indica la condición de aplicación de la regla, es decir que aquí ponemos las condiciones que deben cumplir los objetos para aplicar la regla.
Lo que le estamos diciendo a Drools en este ejemplo es que tome los objetos empleados (de la clase Empleado) y que verifique si el promedio de conocimientos está dentro del rango que estipulamos.
Como se ve, estamos accediendo a evaluar atributos de la clase Empleado que le especificamos.
Con la coma separamos condiciones, que se evalúan como un Y lógico (conjunción).
Aquí también podemos poner disyunciones (O lógico) y negaciones.
En la sección del then especificamos las acciones que ejecutará la regla cuando se cumplan sus condiciones.
En este caso asignamos el cargo y el salario, e imprimimos la regla y el empleado al cual la aplicó (sólo a efectos didácticos de ver que va pasando con las reglas y los objetos).
Como pueden ver, aquí estamos utilizando construcciones java directamente, y lo que hacemos es trabajar con la instancia en la cuál ha aplicado la regla, la cual definimos en el when (empleado).
En este ejemplo sólo llamamos a algunos setters, pero podemos llamar a cualquier método de cualquier objeto que insertemos en la memoria de trabajo, con lo cuál podríamos crear algunos servicios que hicieran algunas operaciones complejas, y llamarlos desde las reglas de Drools.
Si bien en la parte de las acciones se puede escribir cualquier código java, la idea es no usar condicionales. Una regla debe ser declarativa y atómica por naturaleza (cuando pasa esto, entonces hacer esto otro).
Drools evalua cada regla a cada objeto que tenga en la memoria de trabajo, y la aplica a los que correspondan (cumplan la condición).

Uniendo todo

Para simplificar el ejemplo crearemos una única clase de negocio (PoliticaRrhhBo) que será la encargada de obtener los empleados, pasárselos a Drools y ejecutar las reglas, vía la ejecución del main(). Recordemos que aquí el foco está en mostrar el uso de Drools, en un desarrollo real habría que cuidar un poco más el diseño y la codificación.
PoliticaRrhhBo.java:
 
package com.dosideas.drools.ejemplo1.rrhh;
 
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Collection;
import org.drools.RuleBase;
import org.drools.RuleBaseFactory;
import org.drools.WorkingMemory;
import org.drools.compiler.PackageBuilder;
import org.drools.rule.Package; 
 
public class PoliticaRrhhBo {
 
    public static final void main(String[] args) {
        try {
            //Cargamos la base de reglas
            RuleBase ruleBase = leerReglas();
            WorkingMemory workingMemory = ruleBase.newStatefulSession();
 
            //Obtenemos los empleados
            Collection<Empleado> empleados = buscarEmpleados();
 
            for (Empleado empleado : empleados) {
                workingMemory.insert(empleado);
            }
 
            //Disparamos las reglas de Drools
            workingMemory.fireAllRules();
 
            for (Empleado empleado : empleados) {
                System.out.println("Empleado: " + empleado);
            }
 
        } catch (Throwable t) {
            t.printStackTrace();
        }
    }
 
    private static Collection<Empleado> buscarEmpleados() {
        ArrayList<Empleado> empleados = new ArrayList<Empleado>();
 
        //Creamos algunos empleados para el ejemplo
        Empleado empleado1 = new Empleado("Juan", 9);
        Empleado empleado2 = new Empleado("Jose", 6);
        Empleado empleado3 = new Empleado("Pedro", 2);
 
        empleados.add(empleado1);
        empleados.add(empleado2);
        empleados.add(empleado3);
 
        return empleados;
    }
 
    private static RuleBase leerReglas() throws Exception {
        //Leemos el archivo de reglas (DRL)
        Reader source = new InputStreamReader(
            PoliticaRrhhBo.class.getResourceAsStream("PoliticaRrhh_Ej1.drl"));
 
        //Construimos un paquete de reglas
        PackageBuilder builder = new PackageBuilder();
 
        //Parseamos y compilamos las reglas en un único paso
        builder.addPackageFromDrl(source);
 
        // Verificamos el builder para ver si hubo errores
        if (builder.hasErrors()) {
            System.out.println(builder.getErrors().toString());
            throw new RuntimeException(
                "No se pudo compilar el archivo de reglas.");
        }
 
        //Obtenemos el package de reglas compilado
        Package pkg = builder.getPackage();
 
        //Agregamos el paquete a la base de reglas 
        //(desplegamos el paquete de reglas).
        RuleBase ruleBase = RuleBaseFactory.newRuleBase();
        ruleBase.addPackage(pkg);
        return ruleBase;
    }
}
 
Lo que hacemos en esta clase es leer el archivo de reglas PoliticaRrhh_Ej1.drl que acabamos de crear. Con este archivo creamos un paquete de reglas. Para esto Drools analiza y compila el archivo, por lo cuál puede dar errores y los verificamos.
Luego creamos una base de reglas y le agregamos el paquete de reglas que obtuvimos recién.
A esta base de reglas le pedimos una memoria de trabajo. En este caso es del tipo stateful, aunque también podemos utilizar una sesión stateless.
Luego creamos 3 empleados con distintos promedios y los insertamos en la memoria de trabajo. Acto seguido disparamos las reglas de Drools.
Finalmente imprimimos los empleados desde el código java.

El resultado

La salida del programa es la siguiente:
Gerente: Pedro
Lider de Proyecto: Jose
Programador: Juan
Empleado: Juan promedio=9 cargo=Programador salario=1000
Empleado: Jose promedio=6 cargo=Lider de Proyecto salario=2000
Empleado: Pedro promedio=2 cargo=Gerente salario=3000
 
Como se ve se ejecutaron las reglas correspondientes, asignando el cargo de gerente a Pedro, líder de proyecto a Jose, y programador a Juan, con sus respectivos salarios.
Las 3 primeras líneas con los cargos las imprimimos desde el archivo de reglas, y las últimas 3 con los datos de los empleados desde la clase java PoliticaRrhhBo.
Cómo vemos, en la colección empleados de la clase java persisten los cambios que hemos ejecutado desde el archivo de reglas. Esto es porque la memoria de trabajo de Drools funciona con referencias a los objetos.
Es decir que tenemos accesos a la memoria del proceso java en tiempo de ejecución y podemos ejecutar métodos y modificar los estados de los objetos, que luego pueden ser tomados por otras partes de nuestro sistema.
La idea es que podemos modificar nuestro archivo de reglas, agregando nuevas reglas, borrando otras, o modificando las que existen, cambiando tanto los valores o condiciones de entrada a la regla y también pudiendo modificar las acciones tomadas por cada una. Todo esto sin tocar la aplicación (sin compilar ni desplegar).

No hay comentarios:

Publicar un comentario en la entrada