martes, 20 de julio de 2010

SOA y Symfony

Cuando hablamos de Arquitecturas Orientadas a Servicio (SOA - por sus siglas en inglés) es común pensar en Servicios Web y todo lo que conllevan. Mucha gente comienza metiendo Servicios Web por todos lados...

Yo prefiero pensar en arquitecturas donde la lógica de negocio se encuentra perfectamente desacoplada y que luego puede ser aprovechada parte de ésta a través de, por ejemplo SOAP, valiéndose de una fachada.

Para la gente que programa en Java es común pensar en algo como: peticiones HTTP que toma algún framework MVC (Ej. Struts), que delega en Servicios que llaman DAOs, que a su vez se apoyan en un ORM (Ej. Hibernate)... Algo por el estilo, y bueno por supuesto inversión de control (IoC - por sus siglas en inglés) de Spring.

Luego, Symfony ofrece un manejo excepcional MVC, pero... Mucha gente, sobre todo al utilizar formularios olvida la capa de servicios y adiós SOA. A continuación les presento un ejemplo sencillo al respecto:

Primero: Creando la clase de servicio

De momento defineremos una clase Usuario de servicio con un método eliminar (simulado):

//file: /lib/service/user.class.php

class UserService {
 //...
 
 public static function remove($id) {
  //TODO elimina un usuario de BD según el id dado y en caso de error arroja una excepción
 }
 //...
}

Fíjese que el archivo fue colocado dentro de la carpeta lib del proyecto, por ello, Symfony la cargará automáticamente por usted. Además, el método remove es estático porque éste no requiere del estado de un "posible" objeto; generalmente los métodos de servicio son estáticos, puesto que la lógica desarrollada en ellos no requiere estado.

Segundo: El controlador

El código del controlador o lógica de presentación (como prefiero llamarla) deberá colocarlo dentro de su acción, por ejemplo:

//file: /apps/myapp/modules/user/actions/actions.class.php

class userActions extends sfActions
{
 //...

 public function executeRemove(sfWebRequest $request)
 {
  $id = $request->getParameter('id');

  //TODO aquí deberá realizar todas las validaciones pertinentes

  try {
   UserService::remove($id);

   //Manejar el caso de éxito
  } catch (MyCustomException $mce) {
   //TODO manejar la excepción
  }

  //...
 }

 //...

Fíjese que para invocar el método remove de la clase UserService no necesité hacer ningún include o algo parecido. El día de mañana si necesito exponer este método como servicio, simplemente haría una fachada, por ejemplo:

Tercero: La fachada

//file: /lib/soap/user.class.php

class UserSOAP
{
 //...

 public function remove($id)
 {
  //TODO aquí deberá realizar todas las validaciones pertinentes

  try {
   UserService::remove($id);

   //Manejar el caso de éxito
  } catch (MyCustomException $mce) {
   //TODO manejar la excepción
  }

  //...
 }

 //...

Por supuesto que debería utilizar el paquete SOAP de PEAR o algún plugin de Symfony (Ej. ckWebServicePlugin) para que efectivamente esta fachada sea invocada al llamar un Servicio Web.

IMPORTANTE: Sobre todo debe notar que en el execute de mi acción no hay, ni debería haber, ninguna sentencia SQL. Ni siquiera del tipo findBy (métodos de la clase par)... Todas las llamadas a BD deberían estar dentro de servicios, porque sino estaría acoplando la lógica de presentación (execute del action) con la lógica de negocio (service) Y ESO NO SE HACE!:D


No hay comentarios:

Publicar un comentario