Skip to content
José Bocanegra edited this page Jul 14, 2021 · 34 revisions

La capa de lógica

En la arquitectura de capas que estamos utilizando en nuestros proyectos, la capa de lógica de la aplicación o negocio se encarga de coordinar los llamados del api rest y el acceso a la capa de persistencia. Es una capa intermedia entre la implementación de los recursos y la base de datos.

Una de sus responsabilidades es ocuparse de validar reglas de negocio o de invocar servicios de otras aplicaciones. Por ejemplo:

  1. Que el isbn del libro exista en el servicio internacional de isbns.
  2. Que la fecha de publicación del libro sea mayor que la fecha de nacimiento del autor.

La lógica de la aplicación está implementada utilizando Enterprise Java Beans de Sesión sin estado (Stateless EJBs) del API Java Enterprise Edition (JEE) .

A continuación algunos conceptos básicos de los EJBs.

Enterprise Java Beans

Los Enterprise Java Beans proporcionan un modelo de componentes distribuido del lado del servidor. Son unidades modulares reutilizables entre aplicaciones que se ejecutan dentro de un servidor de aplicaciones (por ejemplo, jboss, glassfish, payara, etc.). Permiten que el desarrollador separe el desarrollo de la lógica de negocio de temas como el manejo del ciclo de vida de los beans, la concurrencia, las transacciones, la persistencia, la seguridad, etc. Los EJBs que conforman una aplicación deben ser desplegados dentro de un servidor de aplicaciones que implemente la especificación JEE.

Existen tres tipos de EJBs:

  1. Session beans (pueden ser sin estado Stateless o con estado Stateful),
  2. Entity beans para manejar los datos persistentes (ver Persistencia)
  3. Message-driven beans para manejar mensajes de manera asincrónica.

Los beans de sesión representan elementos fachada que atiende los llamados a los servicios de negocio, es decir, son la fachada al Backend de la aplicación. Dependiendo del diseño de la aplicación puede haber muchos EJBs de sesión; normalmente hay uno por cada módulo funcional que se ocupa de la lógica de negocio del módulo. Los beans de sesión sin estado reciben llamados y devuelven respuestas pero no guardan información del cliente específico que están atendiendo. Los beans de sesión con estado guardan en sus variables de instancia información con el cliente que inició la sesión. Estos valores desaparecen cuando la sesión se termina.

Para propósitos de escalabilidad de una aplicación se utilizan los beans de sesión sin estado para que cualquier bean pueda atender una petición sin necesidad de guardar una relación entre un usuario y un bean particular. En nuestros ejemplos siempre utilizaremos beans de sesión sin estado. Que denotaremos con la anotación @Stateless

Un EJB de sesión es una clase Java anotada adecuadamente para indicar que tipo de EJB de sesión es; esta clase implementa los servicios relacionados en el módulo correspondiente.

El ciclo de vida de los beans de sesión, es decir, la creación/destrucción de las instancias, lo maneja el contenedor. Desde el punto de vista de quien esté utilizando los servicios del bean de sesión, la instancia que lo atiende es indistinguible para él. Por eso, como lo ilustra la Figura 1, para el llamado de un servicio (1) lo atiende un bean A1 pero, para el mismo cliente, cuando invoca otro servicio (2) lo atiende otro bean A2.

Figura 1
Figura 1

Imagen tomada de: http://theopentutorials.com/examples/java-ee/ejb3/stateless-session-bean-pooling-example

La capa de lógica en nuestros proyectos

El siguiente diagrama (Figura 2) ubica los elementos de la arquitectura de las aplicaciones que estamos desarrollando. La capa de lógica contiene los EJBs sin estado y son la fachada para el uso de los servicios que ofrece el Backend de la aplicación. En nuestros proyectos, el cliente del Backend es una API REST (o las pruebas junit). Los objetos que maneja el Backend y transfiere entre las capas son entidades de persistencia, es decir, clase Java anotadas con @Entity.

El Backend no conoce DTOs ni Json ni ninguna otra representación de los recursos.

Figura 2
Figura 2

El diagrama de clases de la Figura 3 presenta para dos módulos, su fachada AuthorLogic, EditorialLogic y su relación con la capa de persistencia. El diagrama no lo muestra pero, cuando la lógica invoca métodos en la persistencia, lo que viaja son entidades. En este caso AuthorEntity y EditorialEntity.

Figura 3
Figura 3

Veamos ahora cómo la lógica se comunica con la persistencia a través de la inyección de dependencias.

Inyección de Dependencias en EJBs

La mayoría de las aplicaciones empresariales Java utilizan recursos y servicios, tales como fuentes de datos, EJBs o servicios web externos. El uso de estos recursos se convierte en algo simple en EJB 3 mediante la inyección de dependencias.

La inyección de dependencias permite que un recurso A declare una dependencia a un recurso B y delega la resolución de la misma al contenedor. El contenedor se ocupa de resolver y entregar una instancia del recurso B y se lo "inyecta" al recurso A. Veamos dos ejemplos: El recurso inyectando la lógica y la lógica inyectando la persistencia.

Ejemplos. Inyección de la lógica en el recurso

En el contexto de nuestro ejemplo company-back tenemos que el recurso CompanyResource define una variable cuyo tipo es una interface de la lógica. Utilizamos la anotación @Inject para indicarle al contenedor que en ejecución "inyecte", es decir, haga que la variable apunte a un objeto de una clase que implementa esa interfaz. El ciclo de vida de ese objeto es manejado por el contenedor de los EJBs. Estos objetos, en nuestros ejemplos, son EJBs, es decir son instancias de clases que han sido anotadas con @Stateless.

@Path("/companies")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class CompanyResource {

    @Inject
    private CompanyLogic companyLogic;
    ... 
}

Inyección de la persistencia en la lógica

La clase de la lógica de company define una variable para acceder a la persistencia de tipo CompanyPersistence. Utilizamos la anotación @Inject para indicarle al contenedor que en ejecución "inyecte", es decir, haga que la variable apunte a un objeto de una clase que CompanyPersistence.

@Stateless
public class CompanyLogic implements ICompanyLogic {

    @Inject private CompanyPersistence persistence;
  ... 
}

Validación de las reglas de negocio

La lógica de la aplicación es responsable de comunicar la capa de servicios (o recursos) con la persistencia. Esta capa no sabe de DTOs y solo manipula objetos Entity. Esta capa es responsable además de verificar reglas de negocio.

Para ilustrar verificaciones de reglas de negocio en nuestro ejemplo de compañias, hemos definido las siguientes:

  1. No puede haber dos compañías con el mismo nombre
  2. No puede haber dos departamentos de la misma compañía con el mismo nombre
  3. Un empleado no puede tener un salario superior a 50 millones.

Vamos a explicar la primera regla de negocio. Esta se debe validar en dos ocasiones: cuando se crea una nueva compañía y cuando se modifica una compañía que ya existe. El siguiente fragmento de código presenta el método en la lógica para crear una compañía.

La lógica recibe el objeto ComanyEntity que se quiere crear, eso se lo envío el API (línea 7). Lo primero que debe hacer es llamar a la persistencia para que consulte en la base de datos si ya existe una compañía con el nombre de la que se quiere crear (línea 8: getCompanyByName). Si ya existe, significa que la regla de negocio no se está validando y en ese caso se dispara la excepción (Línea 10). Si, no existe se procede con la creación de la nueva compañía a través de la persistencia (Línea 13).

@Stateless
public class CompanyLogic {
@Inject
private CompanyPersistence persistence;
...
@Override
public CompanyEntity createCompany(CompanyEntity entity) throws BusinessLogicException {
    CompanyEntity alreadyExist = getCompanyByName(entity.getName());
    if (alreadyExist != null) {
        throw new BusinessLogicException("Ya existe una compañía con ese nombre");
    } else
    {
        persistence.create(entity);
    }
    return entity;
} 
...
}

Para la validación de la segunda regla de negocio: "No puede haber dos departamentos de la misma compañía con el mismo nombre" se debe hacer al crear un departamento en la lógica del departamento.

En este ejemplo, se decidió en el API REST que para crear un departamento se debe hacer a través de la compañía al que pertenece:

POST /companies/123/department

supongamos que el JSON es:

{ "name" : "Relaciones Públicas"}

En la lógica DepartementLogic tenemos el método de creación de un departamento dada una compañía (su id) donde se debe verificar la regla de negocio. El siguiente fragmento de código presenta el método en la lógica para crear un departamento. Note que, debido a nuestro diseño del API, la signatura de método **createDepartment **recibe como parámetro el identificador de la compañía y el entity que viene del API como una conversión del DTO que traía el JSON (Línea 5). En la línea 6 se llama a un método de la persistencia que busca si para la compañía identificada con companyId ya existe un departamento con el nombre dado. Si ya existe, en la línea 8 se dispara la excepción y si no, en la línea 13 se invoca la creación del departamento.

@Stateless
public class DepartmentLogic implements IDepartmentLogic {
...
@Override
public DepartmentEntity createDepartment(Long companyid, DepartmentEntity entity) throws BusinessLogicException {
DepartmentEntity alreadyExist = getDepartmentByName(companyid, entity.getName());
if (alreadyExist != null) {
throw new BusinessLogicException("Ya existe un departamento con ese nombre en la compañía ");
} else {
CompanyEntity company = companyLogic.getCompany(companyid);
entity.setCompany(company);

entity = persistence.create(entity);
}
return entity;
}
...
}

Este libro fue creado para el curso ISIS2603 Desarrollo de Sw en Equipos en la Universidad de los Andes. © Desarrollado por Rubby Casallas con la colaboración de César Forero, Kelly Garcés y Jaime Chavarriaga. Universidad de los Andes, Bogotá, Colombia. 2019

Clone this wiki locally