Selector de Idioma

Puedes cambiar el idioma preferido aquí.

Cloud

He desarrollado múltiples proyectos empleando la tecnología de Google Cloud Platform explotando muchas de las características que ofrece. Mi principal preocupación en cada proyecto siempre es la optimización de los recursos consumidos y los tiempos de respuesta de la aplicación.

Google App Engine

Comencé a trabajar con Google App Engine mientras me encontraba laborando en Orcius, donde después de evaluar las características de los distintos servicios de nube de Microsoft, Amazon y Google, me decanté por éste último debido al ecosistema que ofrecía.

Desde entonces me he actualizado en el uso y manejo de los servicios que ofrece, tales como la construcción de máquinas virtuales, integraciones con API's de mapas y geolocalización, endpoints para aplicaciones móviles, etc. Al tiempo que me he destacado en el diseño y construcción de Bases de Datos Orientadas a Objetos.

Para trabajar con Google Cloud Datastore, he creado mi propio framework a partir de Objectify 5.0, de manera que pueda construir y consumir sus servicios de una manera más rápida y efectiva dentro del código.

¿Objectify?

Una librería de Google para trabajar con Bases de Datos No Relacionales, que actualmente se encuentra en su versión 5. Sin embargo, al día de hoy, el trabajo que se debe realizar para integrarla dentro de un proyecto genera grandes cantidades de código 'ruidoso' dentro de la lógica de trabajo. Motivo por el cual he creado mi propio framework

Generalizando Objectify

Primero y antes que nada, Objectify no maneja tipos durante la compilación, por lo que es fácil confundirse sin darse cuenta, motivo por el cual lo he generalizado la clase y he sobre-escrito los métodos más habituales para tenerlos todos a la mano con el tipo de dato correcto, los métodos más importantes de la clase son los más elementales: add, delete, get, list, listByProperty y se encuentran muchas veces sobre-cargados

	
	public class ObjectifyDao<T> {

	protected Class<T> clazz;

	public ObjectifyDao(Class<T> clazz) {
		this.clazz = clazz;
	}

	public Key<T> add(T entity) {
		Result<Key<T>> result = ObjectifyService.ofy().save().entity(entity);
		return result.now();
	}
	
	public Map<Key<T>, T> add(List<T> counts) {
		Result<Map<Key<T>, T>> result = ObjectifyService.ofy().save().entities(counts);  
		return result.now();
	}

	public void delete(T entity) {
		ObjectifyService.ofy().delete().entity(entity);			
	}
	
	public void delete(Collection<Key<T>> entities) {
		ObjectifyService.ofy().delete().entities(entities);
	}

	public void delete(Key<T> entityKey) {
		ObjectifyService.ofy().delete().key(entityKey);		
	}

	public T get(Key<T> key) throws EntityNotFoundException {
		Result<T> result = ObjectifyService.ofy().load().key(key);
		return result.now();
	}
	
	public Map<Key<T>, T> get(Collection<Key<T>> keys) {
		return ObjectifyService.ofy().load().keys(keys);
	}

	public List<T> getAllList(Key<?> parentKey) {
		Query<T> q = ObjectifyService.ofy().load().type(clazz).ancestor(parentKey);
		return q.list();
	}
	
	public List<T> getAllList(String order, int offset, int limit) {
		Query<T> q = ObjectifyService.ofy().load().type(clazz);
		if (order != null && !order.trim().isEmpty()){
			q = q.order(order);
		}
		q = q.offset(offset).limit(limit);
		return q.list();
	}
	
	...
	

Por otro lado, he creado una interface que cualquier manejador de objetos puede implementar para trabajar con entidades del datastore

	
	public interface EntityService<T>{
		public void setObjectifyDao(ObjectifyDao<T> ofyDao);
		public T load(Key<T> key);
		public T load(String key);
		public Map<Key<T>, T> load(Collection<Key<T>> keys);
		public Key<T> save(T entity);
		public Map<Key<T>, T> save(List<T> entity);
		public void delete(Key<T> key);
		public void deleteEntities(List<Keyable<T>> keys);
		public void delete(List<Key<T>> keys);
		public List<T> list(Key<?> parent);
		public List<T> list(Key<?> parent, String order, int offset, int limit);
		public List<T> list(Key<?> parent, int offset, int limit);
		public List<T> list();
		public List<T> list(int offset, int limit);
		public List<T> list(String order, int offset, int limit);
		public boolean isHardRequired();
		public void setHardRequired(boolean hardRequired);	
	}
	

Siendo que muchas entidades deberían implementar ésta interface para procedimientos equivalentes, creé una clase de la que pueden heredar y que realiza muchos de los procesos comunes:

	
	public abstract class EntityServiceImpl<T> implements EntityService<T> {
	
		protected ObjectifyDao<T> dao;
		protected boolean hardRequired = false;
		
		public boolean isHardRequired() { return hardRequired; };
		public void setHardRequired(boolean hardRequired) {
			this.hardRequired = hardRequired;
		}
		
		public void setObjectifyDao(ObjectifyDao<T> objectifyDao) {
			this.dao = objectifyDao;
		}
		
		@Override
		public T load(Key<T> key) {
			try {
				return isHardRequired() ? dao.getHard(key) : dao.get(key);
			} catch (Exception e) {
				return null;
			}
		}
	
		@Override
		public T load(String keyString) {
			if (keyString != null && !keyString.trim().isEmpty()){
				Key<T> key = Key.create(keyString);
				return load(key);
			}
			return null;
		}
		
		@Override
		public Map<Key<T>, T> load(Collection<Key<T>> keys) {
			return dao.get(keys);
		}
	
		@Override
		public Key<T> save(T entity) {
			return isHardRequired() ? dao.addHard(entity) : dao.add(entity);
		}
		
		@Override
		public Map<Key<T>, T> save(List<T> entity) {
			return dao.add(entity);
		}
		
		@Override
		public void delete(Key<T> key) {
			dao.delete(key);
		}
		
		public void deleteEntities(List<Keyable<T>> objects) {
			List<Key<T>> keys = new ArrayList<>(objects.size());
			for (Keyable<T> o : objects) {
				keys.add(o.getKey());
			}
			dao.delete(keys);
		}
		
		public void delete(List<Key<T>> keys) {
			dao.delete(keys);
		}
		
		@Override
		public List<T> list(Key<?> parentKey) {
			if (parentKey != null) {
				return dao.getByParentId(parentKey);
			} else {
				return dao.getAllList();
			}
		}
		
		@Override
		public List<T> list(Key<?> parentKey, int offset, int limit) {
			return list(parentKey, null, offset, limit);
		}
		
		@Override
		public List<T> list(Key<?> parentKey, String order, int offset, int limit) {
			if (parentKey != null) {
				return dao.getList(parentKey, order, offset, limit);
			} else {
				return dao.getAllList(order, offset, limit);
			}
		}
		
		@Override
		public List<T> list() {
			return dao.getAllList();
		}
		
		@Override
		public List<T> list(int offset, int limit) {
			return list("", offset, limit);
		}
		
		@Override
		public List<T> list(String order, int offset, int limit) {
			return dao.getAllList(order, offset, limit);
		}	
	}
	

Éstas clases e interfaces se encuentran definidas dentro de mis propias librerías, por lo que en cualquier proyecto que desee implementarlas, sólo debe crear la clase de entidad y definir los servicios con un mínimo de líneas de código

EntityClass, EntityService y EntityServiceImpl

Partiendo de una clase EntidadEjemplo, se puede construir un proceso muy simple para acceder al Datastore

	
	@Entity
	public class EntidadEjemplo {
		@Id private Long id;
		private String dato;
		@Index private Date fecha;
		
		public Long getId() { return id; }
		public void setId(Long id) { this.id = id; }
		
		public String getDato() { return dato; }
		public void setDato(String dato) { this.dato = dato; }
		
		public Date getFecha() { return fecha; }
		public void setFecha(Date fecha) { this.fecha = fecha; }
	}
	

Creo una interfaz que me pueda permitir personalizar posteriormente los comportamientos, de momento, sólo heredo de EntityService

	
	public interface EntidadEjemploService extends EntityService<EntidadEjemplo> {}
	

Creo una clase que implemente la interfaz que acabo de crear, heredando de EntityServiceImpl

	
	public class EntidadEjemploServiceImpl implements EntidadEjemploService extends EntityServiceImpl<EntidadEjemplo> {}
	

Con ésto, sin más líneas de código, puedo acceder a los métodos básicos de Objectify que he sobrecargado, de manera tipificada en cualquier bloque que requiera

	
	public class FuncionesEjemplo {
		private EntidadEjemploService ees = ServiceFactory.getInstance().getEntidadEjemploService();
		
		public String registrarEjemplo(Date hoy, String dato) {
			EntidadEjemplo ee = ees.getByProperty("fecha", hoy);
			if (ee == null) {
				ee = new EntidadEjemplo();
			}
			ee.setDato(dato);
			Key<EntidadEjemplo> key = ees.save(dato);
			return key.getString();
		}
	}
	
Configuraciones de Spring

Como parte de las configuraciones adicionales que requiere Objectify, es que se inicialice con cada una de las entidades que deseemos leer / escribir, por lo que cree una clase OfyService que mantiene un singleton inicializado desde que se arranca la ejecución de la app con un objeto de Objectify y registro las clases con las que trabajaré

Finalmente, como en el ejemplo, normalmente mantengo singleton ServiceFactory que me permita acceder a los servicios donde quiera que esté en el código del proyecto

	
	...
	<bean id="OfyService" class="com.genexis.dao.OfyService" factory-method="getInstance">
		<constructor-arg name="classes">
			<array value-type="java.lang.Class">
				<value>com.genexis.ccorona.entity.EntidadEjemplo</value>
				...
			</array>
		</constructor-arg>
	</bean>
	...
	
	<bean id="entidadEjemploDao" class="com.genexis.dao.ObjectifyDao">
	    <constructor-arg name="clazz" value="com.genexis.ccorona.entity.EntidadEjemplo" />
	</bean>
	<bean id="entidadEjemploService" class="com.genexis.ccorona.service.impl.EntidadEjemploServiceImpl">
	    <property name="objectifyDao" ref="entidadEjemploDao" />
	</bean>
	
	<bean id="ServiceFactory" class="com.genexis.ServiceFactory" factory-method="getInstance">
		<property name="entidadEjemploService" ref="entidadEjemploService" />
		...
	</bean>