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