Lorenzo Gonzalez Gascón
I'm teacher in computer Science in a high school, the high school is named "CIPFP Mislata" in Spain-Valencia-Mislata .
Madrid , 9 de Mayo de 2015
¿Quieres que mis alumnos hagan las prácticas en tu empresa?
¡Ponte en contacto conmigo!
¿Lo obvio no es añadir una capa de persistencia?
Evita repetir código en los DAO
Realiza la coordinación entre DAOs
Enriquece el modelo
Unificar comportamiento entre DAOs
Solo realizan una única tarea:
HTTP,
HTML5 Storage,
etc.
NOTA:Obviamente en JavaScript no hay interfaces, pero es para que se entienda
Inicialmente no hace nada solo pasa datos pero sirve para
poder modificarla libremente
MiClase.$inject=['$http','$log']
function MiClase($http,$log) {
}
var miClase = $injector.instantiate(MiClase);
Definir dependencias en una clase
Crear una instancia
var locals = {
$log: miNuevoLog
};
var miClase = $injector.instantiate(MiClase,locals);
Sobreescribir inyecciones (realmente pasar parámetros)
var miFuncion=['$http','$log',function($http,$log) {
}]
$injector.invoke(miFuncion,elThis);
Función con inyección de dependencias
Ejecutar la función
var locals = {
$log: miNuevoLog
};
var resultado = $injector.invoke(miFuncion,elThis,locals);
Sobreescribir inyecciones (realmente pasar parámetros)
var urlExpression=$interpolate("/api/{{entityName}}/{{id}}");
var urlUsuario=urlExpression({
entityName:"Usuario",
id:3
});
var urlEmpresa=urlExpression({
entityName:"Empresa",
id:12
});
urlUsuario="/api/Usuario/3" urlEmpresa="/api/Empresa/12"
El resultado es:
La expresión se puede reusar
Modifica la rama "master" para hacer que se usen 3 serviciós de AngularJS:
La solución está en la rama "tres_capas_horror"
angular.module("name").service("usuarioRemoteDAO", UsuarioRemoteDAO);
angular.module("name").service("compraRemoteDAO", CompraRemoteDAO);
angular.module("name").service("movimientoRemoteDAO", MovimientoRemoteDAO);
angular.module("name").service("rolRemoteDAO", RolRemoteDAO);
angular.module("name").service("tarjetaRemoteDAO", TarjetaRemoteDAO);
angular.module("name").service("alumnoRemoteDAO", AlumnoRemoteDAO);
angular.module("name").service("usuarioRepository", UsuarioRepository);
angular.module("name").service("compraRepository", CompraRepository);
angular.module("name").service("movimientoRepository", MovimientoRepository);
angular.module("name").service("rolRepository", RolRepository);
angular.module("name").service("tarjetaRepository", TarjetaRepository);
angular.module("name").service("alumnoRepository", AlumnoRepository);
angular.module("name").service("usuarioService", UsuarioService);
angular.module("name").service("compraService", CompraService);
angular.module("name").service("movimientoService", MovimientoService);
angular.module("name").service("rolService", RolService);
angular.module("name").service("tarjetaService", TarjetaService);
angular.module("name").service("alumnoService", AlumnoService);
angular.module("name").service("serviceFactory", ServiceFactory);
angular.module("name").service("repositoryFactory", RepositoryFactory);
angular.module("name").service("remoteDAOFactory", RemoteDAOFactory);
Únicamente 3 servicios de AngularJS
UsuarioRepository.$inject=['remoteDAOFactory'];
function UsuarioRepository(remoteDAOFactory) {
var usuarioRemoteDAO=remoteDAOFactory.getRemoteDAO("Usuario");
var promise=usuarioRemoteDAO.get(3);
}
Se llama a getRemoteDAO(entidad)
RemoteDAO.$inject = ['$http', 'entityName'];
function RemoteDAO($http, entityName) {
this.entityName=entityName
this.get = function (id) {
var promise=$http({
method:"GET",
url:"/api/" + entityName + "/" + id;
});
//Implementar el método
};
}
RemoteDAOFactory.$inject = ['$injector'];
function RemoteDAOFactory($injector) {
var remoteDAOs = {
};
this.getRemoteDAO = function (entityName) {
if (!remoteDAOs[entityName]) {
var locals = {
entityName: entityName
};
remoteDAOs[entityName] = $injector.instantiate(RemoteDAO,locals);
}
return remoteDAOs[entityName];
};
}
Modifica la rama "tres_capas_horror" para hacer que se usen los 3 servición de AngularJS en vez de los anteriores:
La solución está en la rama "simple_factory"
<--- Mi solución
Para el PUT y el DELETE /entidad/id
El DAOFactory es un "provider" de AngularJS
remoteDAOFactoryProvider.setExtendRemoteDAO("Usuario",['remoteDAO',function(remoteDAO) {
remoteDAO.deshabilitar=function(usuario) {
//Aqui el código específico del nuevo método del DAO
};
}]);
Añadimos el método "deshabilitar" al remoteDAO de Usuario
Cuando se cree un nuevo UsuarioRemoteDAO se llamará a la función para que pueda añadir los métodos que quiera al RemoteDAO
RemoteDAOFactory.$inject = ['$injector', 'extendRemoteDAO'];
function RemoteDAOFactory($injector, extendRemoteDAO) {
var remoteDAOs = {
};
this.getRemoteDAO = function (entityName) {
if (!remoteDAOs[entityName]) {
var locals = {
entityName: entityName
};
remoteDAOs[entityName] = $injector.instantiate(RemoteDAO,locals);
if (extendRemoteDAO[entityName]) {
var locals = {
remoteDAO: remoteDAOs[entityName]
};
$injector.invoke(extendRemoteDAO[entityName], undefined, locals);
}
}
return remoteDAOs[entityName];
};
}
Al crear el RemoteDAO llamamos a una función para que lo "extienda" como quiera
RemoteDAOFactoryProvider.$inject = ['$injector'];
function RemoteDAOFactoryProvider() {
var extendRemoteDAO = {
};
this.setExtendRemoteDAO = function (entityName, fn) {
extendRemoteDAO[entityName] = fn;
};
this.$get = ['$injector', function ($injector) {
var locals = {
extendRemoteDAO: extendRemoteDAO
};
return $injector.instantiate(RemoteDAOFactory, locals);
}];
}
RemoteDAO.$inject = ['$http', '$interpolate', 'entityName'];
function RemoteDAO($http, $interpolate, entityName) {
this.entityName=entityName
this.urlGet="/api/{{entityName}}/{{id}}";
this.urlGetExpression=$interpolate(this.urlGet);
this.get = function (id) {
var url=urlGetExpression({
entityName:this.entityName,
id:id
});
$http({
method:"GET",
url:url
});
//Implementar todo el método
};
}
remoteDAOFactoryProvider.setExtendRemoteDAO(
"Usuario",['remoteDAO','$interpolate',function(remoteDAO,$interpolate) {
remoteDAO.urlGetExpression=$interpolate("/api/v2/{{entityName}}/{{id}}");
}]);
RemoteDAO.$inject = ['$http', '$interpolate', 'entityName'];
function RemoteDAO($http, $interpolate, entityName) {
this.entityName=entityName
this.urlGet="/api/{{entityName}}/{{id}}";
this.urlGetExpression=$interpolate(this.urlGet);
this.preGet=function() {};
this.postGet=function() {};
this.get = function (id) {
var url=urlGetExpression({
entityName:this.entityName,
id:id
});
this.preGet();
$http({
method:"GET",
url:url
});
this.postGet(); //OJO:Debe estar en la promesa
//Implementar todo el método
};
}
remoteDAOFactoryProvider.setExtendRemoteDAO(
"Usuario",['remoteDAO','$interpolate',function(remoteDAO,$interpolate) {
remoteDAO.postGet=function() {
alert("Leido usuario");
}
}]);
RemoteDAOFactory.$inject = ['$injector'];
function RemoteDAOFactory($injector) {
var remoteDAOs = {
};
this.getRemoteDAO = function (entityName) {
if (!remoteDAOs[entityName]) {
if ($injector.has(entityName + "RemoteDAO") {
remoteDAOs[entityName] = $injector.get(entityName + "RemoteDAO");
} else {
var locals = {
entityName: entityName
};
remoteDAOs[entityName] = $injector.instantiate(RemoteDAO,locals);
}
}
return remoteDAOs[entityName];
};
}
Si queremos personalizar el RemoteDAO de "Usuario" creamos el servicio "UsuarioRemoteDAO"
No he encontrado ninguna solución que sea:
buena, bonita y barata
:-(
Si sabes lo que estás haciendo puedes tener soluciones sencillas para la casuística de tu proyecto.
Modifica la rama "simple_factory" para hacer que ahora los "Service", "Repository" y "RemoteDAO" se puedan extender.
La solución está en la rama "factory_con_providers_completo"
Es necesario enriquecer el DTO y obtener el modelo rico
var dtoUsuario1 = {
nombre: "Marcos",
ape1: "Salas",
ape2: "Lopez",
fechaNacimiento: 31548664546,
tipoUsuario: "ALUMNO"
};
var dtoUsuario2 = {
nombre: "Carlos",
ape1: "Diaz",
ape2: "Ortega",
fechaNacimiento: 64874654646,
tipoUsuario: "PROFESOR",
departamento: 3
};
var modelUsuario1 = {
nombre: "Marcos",
ape1: "Salas",
ape2: "Lopez",
getNombreCompleto: function () {
return this.nombre + " " + this.ape1 + " " + this.ape2;
},
fechaNacimiento: new Date(31548664546),
tipoUsuario: "ALUMNO"
};
var modelUsuario2 = {
nombre: "Carlos",
ape1: "Diaz",
ape2: "Ortega",
getNombreCompleto: function () {
return this.nombre + " " + this.ape1 + " " + this.ape2;
},
fechaNacimiento: new Date(64874654646),
tipoUsuario: "PROFESOR",
departamento: 3,
getNombreDepartamento: function () {
return departamentoService.getNombre(this.departamento);
}
};
app.config(['richModelProvider', function(richModelProvider) {
richModelProvider.addGlobalTransformer(function() {
var uniqueID=0;
return function (object) {
object.$uniqueID=uniqueID;
uniqueID++;
};
});
}]);
La primera función se ejecuta una única vez y después de la fase de configuración por lo que permite inyectar servicios. En ella se define una única vez lo que se va a enriquecer.La función que retorna es llamada para cada objeto a enriquecer
app.config(['richModelProvider', function(richModelProvider) {
richModelProvider.addEntityTransformer('Usuario',['departamentoService',function(departamentoService) {
function getNombreCompleto () {
return this.nombre + " " + this.ape1 + " " + this.ape2;
}
function getNombreDepartamento () {
return departamentoService.getNombre(this.departamento);
}
return function (object) {
object.getNombreCompleto = getNombreCompleto;
if (object.tipoUsuario === "PROFESOR") {
object.getNombreDepartamento = getNombreDepartamento;
}
object.fechaNacimiento = new Date(object.fechaNacimiento);
};
}]);
}]);
¡No queremos repetir código ni preocuparnos por las relaciones!
var modelUsuario2 = {
nombre: "Carlos",
ape1: "Diaz",
ape2: "Ortega",
getNombreCompleto: function () {
return this.nombre + " " + this.ape1 + " " + this.ape2;
},
fechaNacimiento: new Date(64874654646),
tipoUsuario: "PROFESOR",
departamento: 3,
getNombreDepartamento: function () {
return departamentoService.getNombre(this.departamento);
},
centro: {
codigo:"46567",
nombre:"CIFP Mislata",
getCodigoProvincia: function() {
return this.codigo.substring(0,2);
}
}
};
var modelAula = {
nombre:"2A5",
piso:2,
centro: {
codigo:"46567",
nombre:"CIFP Mislata",
getCodigoProvincia: function() {
return this.codigo.substring(0,2);
}
}
};
Hay que repetir el código en cada sitio donde aparece una misma entidad
app.config(['richModelProvider', function(richModelProvider) {
richModelProvider.addEntityTransformer('Usuario',function() {
function getCodigoProvincia() {
return this.codigo.substring(0,2);
}
return function (object) {
object.centro.getCodigoProvincia=getCodigoProvincia;
};
});
}]);
Conocer la relación entre Usuario y Centro
app.config(['richModelProvider', function(richModelProvider) {
richModelProvider.addEntityTransformer('Aula',function() {
function getCodigoProvincia() {
return this.codigo.substring(0,2);
}
return function (object) {
object.centro.getCodigoProvincia=getCodigoProvincia;
};
});
}]);
Conocer la relación entre Aula y Centro
app.config(['richModelProvider', function(richModelProvider) {
richModelProvider.addEntityTransformer('Centro',function() {
function getCodigoProvincia() {
return this.codigo.substring(0,2);
}
return function (object) {
object.getCodigoProvincia=getCodigoProvincia;
};
});
}]);
Enriquecer directamente la entidad "Centro"
Desde la parte Servidora (Java) si que tengo tipos así que mis JSON siempre incluyen el nombre de la clase
var modelUsuario2 = {
nombre: "Carlos",
ape1: "Diaz",
ape2: "Ortega",
fechaNacimiento: 64874654646,
tipoUsuario: "PROFESOR",
departamento: 3,
$className:"Usuario",
centro: {
codigo:"46567",
nombre:"CIFP Mislata",
$className:"Centro"
}
};
var modelAula = {
nombre:"2A5",
piso:2,
$className:"Aula",
centro: {
codigo:"46567",
nombre:"CIFP Mislata",
$className:"Centro"
}
};
Ya puedo enriquecer "Centro" independientemente de donde esté
Desde la rama "factory_con_providers_completo", crea el provider de AngularJS llamado "richModel" y enriquece el usuario con los métodos que se indicaban al inicio de esta sección
A "richModel" se llamará desde "repository"
La solución está en la rama "rich_domain"
app.config(['richModelProvider', function(richModelProvider) {
richModelProvider.addEntityTransformer('Usuario',function() {
var $validators = [
{
propertyName: function() {
return "Confirmar Contraseña"
},
message: 'El valor de {{password}} no es igual al de {{confirmPassword}}',
rule: function () {
if (this.password === this.confirmPassword) {
return true;
} else {
return false;
}
}
}
]
return function (object) {
object.$validators=$validators;
};
});
}]);
Yo uso Repository para dejar limpio Service para que se pueda modificar libremente
Desde la rama "rich_domain", crea el servicio de AngularJS llamado "modelValidator" y enriquece el usuario con la validación de confirmar la contraseña
A "modelValidator" se llamará desde "repository"
Tambien añade el método "update" en las 3 capas y muestra los mensajes de error en el HTML
La solución está en la rama "rich_domain"
Mas en: http://www.cursoangularjs.es
By Lorenzo Gonzalez Gascón
Transparencias del JSDay sobre el taller deArquitectura en 3 capas y modelos ricos en AngularJS
I'm teacher in computer Science in a high school, the high school is named "CIPFP Mislata" in Spain-Valencia-Mislata .