

- Para dirigir el flujo de un proceso de negocio. A este tipo de reglas de negocio se les denomina reglas con lógica de decisión («Decision logic rules»).
- Para tratar información en base a definiciones, operaciones y restricciones. A este tipo de reglas se les denomina reglas con lógica de negocio («Business logic rules»).
- Si el cliente es VIP enviar paquete por transporte urgente.
- Si el cliente ha realizado una compra por importe superior a 100€ el cliente es VIP.
- Si el cliente es VIP aplicar un descuento de 10€.
Algunas ventajas de las reglas de negocio son:
- Están documentadas y puede ser gestionadas, no es un conocimiento que posean las personas en sus cabezas.
- Las reglas de negocio puede estar escritas en un lenguaje entendible por las personas de negocio. La gente de negocio puede verificar la lógica y cambiarla.
- No se utiliza código de programa en algún lenguaje de programación que probablemente con el tiempo se convierta en código spaghetti y que no es entendible por las personas de negocio.
- Es más fácil, flexible y rápido modificar las reglas de negocio que el código de programa equivalente en Java o C#. Se pueden modificar independientemente del código ya que las reglas no se encuentran en la aplicación.
Veamos ahora un ejemplo similar al realizado en Usar variables en un proceso de negocio con Activiti pero usando reglas de negocio. El diagrama del proceso de negocio es el siguiente:
Veamos ahora el proceso de negocio en notación BPMN:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:activiti="http://activiti.org/bpmn" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL http://www.omg.org/spec/BPMN/2.0/20100501/BPMN20.xsd" targetNamespace="HelloWorldActiviti"> | |
<process id="reglasDeNegocio" name="Reglas de negocio" isExecutable="true"> | |
<startEvent id="start" /> | |
<sequenceFlow id="flow1" sourceRef="start" targetRef="determinarTipoClienteTask" /> | |
<businessRuleTask id="determinarTipoClienteTask" activiti:ruleVariablesInput="${cliente},${importe}" activiti:rules="TipoClienteVIP,TipoClienteNormal" /> | |
<sequenceFlow id="flow2" sourceRef="determinarTipoClienteTask" targetRef="determinarDescuentoTask" /> | |
<businessRuleTask id="determinarDescuentoTask" activiti:ruleVariablesInput="${cliente},${output}" activiti:rules="DescuentoClienteVIP,DescuentoClienteNormal" /> | |
<sequenceFlow id="flow3" sourceRef="determinarDescuentoTask" targetRef="determinarTipoEnvioTask" /> | |
<businessRuleTask id="determinarTipoEnvioTask" activiti:ruleVariablesInput="${cliente},${output}" activiti:rules="TipoEnvioClienteVIP,TipoEnvioClienteNormal" /> | |
<sequenceFlow id="flow4" sourceRef="determinarTipoEnvioTask" targetRef="tipoEnvioGateway" /> | |
<exclusiveGateway id="tipoEnvioGateway" /> | |
<sequenceFlow sourceRef="tipoEnvioGateway" targetRef="envioNormalTask"> | |
<conditionExpression xsi:type="tFormalExpression">${output.tipoEnvio == 'normal'}</conditionExpression> | |
</sequenceFlow> | |
<sequenceFlow sourceRef="tipoEnvioGateway" targetRef="envioUrgenteTask"> | |
<conditionExpression xsi:type="tFormalExpression">${output.tipoEnvio == 'urgente'}</conditionExpression> | |
</sequenceFlow> | |
<scriptTask id="envioNormalTask" scriptFormat="groovy"> | |
<script>println('Envío normal')</script> | |
</scriptTask> | |
<sequenceFlow id="flow5" sourceRef="envioNormalTask" targetRef="end" /> | |
<scriptTask id="envioUrgenteTask" scriptFormat="groovy"> | |
<script>println('Envío urgente')</script> | |
</scriptTask> | |
<sequenceFlow id="flow6" sourceRef="envioUrgenteTask" targetRef="end" /> | |
<endEvent id="end" /> | |
</process> | |
</definitions> |
Ahora el código de las reglas de negocio para determinar el tipo de cliente, las reglas para determinar el descuento y el tipo de envío son muy parecidas y pueden verse en el código fuente del ejemplo:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package es.com.blogspot.elblogdepicodev.activiti | |
import java.math.BigDecimal; | |
import es.com.blogspot.elblogdepicodev.activiti.misc.Cliente; | |
import es.com.blogspot.elblogdepicodev.activiti.misc.Cliente.Tipo; | |
rule "TipoClienteVIP" | |
when | |
BigDecimal(this.compareTo(new BigDecimal('100')) == 1) | |
c: Cliente() | |
then | |
c.setTipo(Tipo.VIP); | |
end | |
rule "TipoClienteNormal" | |
when | |
BigDecimal(this.compareTo(new BigDecimal('100')) <= 0) | |
c: Cliente() | |
then | |
c.setTipo(Tipo.NORMAL); | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package es.com.blogspot.elblogdepicodev.activiti; | |
import java.math.BigDecimal; | |
import java.util.HashMap; | |
import java.util.Map; | |
import org.activiti.engine.RuntimeService; | |
import org.activiti.engine.test.ActivitiRule; | |
import org.activiti.engine.test.Deployment; | |
import org.junit.Assert; | |
import org.junit.Rule; | |
import org.junit.Test; | |
import es.com.blogspot.elblogdepicodev.activiti.misc.Cliente; | |
import es.com.blogspot.elblogdepicodev.activiti.misc.Cliente.Tipo; | |
@SuppressWarnings({ "rawtypes", "unchecked" }) | |
public class ReglasDeNegocioTest extends ActivitiAbstractTest { | |
@Rule | |
public ActivitiRule activitiRule = new ActivitiRule("activiti-h2.cfg.xml"); | |
@Test | |
@Deployment(resources = { "bpmn/ReglasDeNegocio.bpmn20.xml", "rules/TipoCliente.drl", "rules/Descuento.drl", "rules/TipoEnvio.drl" }) | |
public void testNormal() { | |
Cliente cliente = new Cliente(); | |
Map output = new HashMap(); | |
Map variables = new HashMap(); | |
variables.put("cliente", cliente); | |
variables.put("importe", new BigDecimal("50")); | |
variables.put("output", output); | |
RuntimeService rs = activitiRule.getRuntimeService(); | |
rs.startProcessInstanceByKey("reglasDeNegocio", variables); | |
Assert.assertEquals(cliente.getTipo(), Tipo.NORMAL); | |
Assert.assertEquals(BigDecimal.ZERO, output.get("descuento")); | |
Assert.assertEquals("normal", output.get("tipoEnvio")); | |
} | |
@Test | |
@Deployment(resources = { "bpmn/ReglasDeNegocio.bpmn20.xml", "rules/TipoCliente.drl", "rules/Descuento.drl", "rules/TipoEnvio.drl" }) | |
public void testVIP() { | |
Cliente cliente = new Cliente(); | |
Map output = new HashMap(); | |
Map variables = new HashMap(); | |
variables.put("cliente", cliente); | |
variables.put("importe", new BigDecimal("150")); | |
variables.put("output", output); | |
RuntimeService rs = activitiRule.getRuntimeService(); | |
rs.startProcessInstanceByKey("reglasDeNegocio", variables); | |
Assert.assertEquals(cliente.getTipo(), Tipo.VIP); | |
Assert.assertEquals(new BigDecimal("10"), output.get("descuento")); | |
Assert.assertEquals("urgente", output.get("tipoEnvio")); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> | |
<bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration"> | |
... | |
<property name="customPostDeployers"> | |
<list> | |
<ref bean="rulesDeployer" /> | |
</list> | |
</property> | |
</bean> | |
... | |
<bean id="rulesDeployer" class="org.activiti.engine.impl.rules.RulesDeployer" /> | |
</beans> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package es.com.blogspot.elblogdepicodev.activiti; | |
import java.math.BigDecimal; | |
import java.util.HashMap; | |
import java.util.Map; | |
import org.activiti.engine.ProcessEngine; | |
import org.activiti.engine.ProcessEngineConfiguration; | |
import org.activiti.engine.ProcessEngines; | |
import org.activiti.engine.RepositoryService; | |
import org.activiti.engine.RuntimeService; | |
import org.activiti.engine.runtime.ProcessInstance; | |
import org.h2.tools.Server; | |
import es.com.blogspot.elblogdepicodev.activiti.misc.Cliente; | |
import es.com.blogspot.elblogdepicodev.activiti.misc.Cliente.Tipo; | |
public class ReglasDeNegocio { | |
public static void main(String[] args) throws Exception { | |
Server server = null; | |
try { | |
server = Server.createTcpServer().start(); | |
ProcessEngines.init(); | |
ProcessEngine processEngine = ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti-mysql.cfg.xml").buildProcessEngine(); | |
RuntimeService runtimeService = processEngine.getRuntimeService(); | |
RepositoryService repositoryService = processEngine.getRepositoryService(); | |
repositoryService.createDeployment().addClasspathResource("bpmn/ReglasDeNegocio.bpmn20.xml").addClasspathResource("rules/TipoCliente.drl") | |
.addClasspathResource("rules/Descuento.drl").addClasspathResource("rules/TipoEnvio.drl").deploy(); | |
Cliente cliente = new Cliente(Tipo.NORMAL); | |
Map variables = new HashMap(); | |
Map output = new HashMap(); | |
variables.put("cliente", cliente); | |
variables.put("importe", new BigDecimal("150")); | |
variables.put("output", output); | |
ProcessInstance pi = runtimeService.startProcessInstanceByKey("reglasDeNegocio", variables); | |
System.out.println("Tipo cliente: " + cliente.getTipo()); | |
System.out.println("Descuento aplicado: " + output.get("descuento")); | |
} finally { | |
ProcessEngines.destroy(); | |
if (server != null) | |
server.stop(); | |
} | |
} | |
} |
Referencia:
Conceptos sobre procesos de negocio (BP, BPM, BPMS, ...)
Procesos de negocio con Activiti
Usar variables en un proceso de negocio con Activiti
Tratamiento de errores en procesos de negocio con Activiti
Código fuente del ejemplo Reglas de Negocio con Activiti