Entity Provider Pattern
Estoy intentando hacer un pattern para lograr crear servicios que se puedan crear desde la estructura de una entidad (representada por una transacción).
La documentación va ir quedando aqui. Copio aqui la información para que quede documentado.
Objetivo:
Facilitar y centralizar el acceso y modificación de los datos de una entidad.
Uso
Dada una Entidad (en forma de transacción) facilitar el acceso a sus datos con operaciones comunes.
Ejemplo
Dada la transacción Customer
Customer *CustomerId CustomerName //tiene indice unico por CustomerName CustomerAddress CustomerType
se pueden poner dicha transacción como BC y generar los objetos para lograr:
//CONSULTAS (QUERIES)
&CustomerCollection = GetAll() // Se implementa con un Customer_DP - Data provider
&CustomerCollection = GetBy<Condition>(&ConditionParameters) // Se implementa con un Customer_DP<Condition> - DataProvider que usa un Data Selector por <Condition>
Customer_DS_Condition(&ConditionParameters) // Data Selector por Condition , &ConditionParameters es un SDT con los parámetros de la condicion
&CustomerCollection = GetByType(&CustomerType) // Se implementa con un DP que filtra por Type
&Customer = GetByKey(&CustomerId) // Se implementa con el BC de Customer
&Customer = GetByName(&CustomerName) // Se implementa con un Data Provider que filtra por nombre
&CustomerType = GetType(&CustomerId) // Procedure que hace un for each por &CustomerID y devuelve el CustomerType.
//MODIFICACIONES (MUTATIONS)
CustomerInsert(&Customer,&Messages) // Se implementa con un Procedure que usa el BC para hacer insert
CustomerDelete(&Customer,&Messages) //igual que el anterior
CustomerUpdate(&Customer,&Messages) //Igual que el anterior
CustomerSetType(&CustomerID, &CustomerType, &Messages) //BC que hace un Get con &CustomerID, actualiza el &CustomerType y hace el Update
CustomerSetName(&CustomerID, &CustomerName,&Messages) // igual que el anterior.
Tambien generara el objeto API que engloba todo el acceso.
Customer{ //Queries - Only Read Data [RestVerb(GET)] GetAll(out:&CustomerCollection) => CustomerGetAll(0, 99999999, &CustomerCollection); GetAll_Paginated(in:&PageNumber, in:&PageSize, out:&CustomerCollection) => CustomerGetAll(&PageNumber, &PageSize, out:&CustomerCollection); GetByKey(in:&CustomerId,out:&Customer) => CustomerGetByKey(&CustomerId,&Customer) ; GetCustomerType(in:&CustomerId,out:&CustomerType) => CustomerGetType(&CustomerId,&CustomerType); Exist(in:&CustomerId,out:&CustomerExist) => CustomerExist(&CustomerId,&CustomerExist); //Mutations - Change Data [RestVerb(POST) ] Insert(in:&Customer, out:&Messages) => CustomerInsert(&Customer,&Messages) ; [RestVerb(DELETE), RestPath /Customer/&CustomerID] Delete(in:&CustomerID,out:&Messages) => CustomerDelete(&CustomerID, &Messages); [RestVerb(PUT)] Update(in:&Customer,out:&Messages) => CustomerUpdate(&Customer, &Messages); [RestVerb(PUT)] SetCustomerType(in:&CustomerID, in:&CustomerType) => CustomerSetType(&CustomerId,&CustomerType); }
Lista de Objetos que se necesitan
- &Customer - Variable basada en BC Customer
- &CustomerCollection - Collection de &Customer
- &CustomerID - Basada en CustomerID - Puede ser &CustomerKEY SDT en caso de ser una clave compuesta, para tener menos parametros.
- CustomerDPGetAll - Data Provider que devuelve todos los Customers
- CustomerDPGetByKey - Data Provider que devuelve un Customer por clave
- CustomerDPGetBy<Condition>(&Customer<Condition>Filters) - Data Provider que devuelve collection de Customers, dada una condicion
- CustomerDS<Condition>(&Customer<Condition>Filters) - Data Selector que filtra por la condicion especificada.
- &Customer<Condition>Filters - SDT que tiene la lista de parametros de DS correspondiente a <Condition>
- CustomerGetType - Procedure que recibe una KEY devuelve el Att CustomerType // De estos pueden haber varios para cada uno de los atributos secundarios que se necesiten.
- CustomerInsert, CustomerUpdate, CustomerDelete - son procedures que usan los BC para hacer el ABM de la tabla Customer
- CustomerSet<Att> - Procedure que recibe una KEY y el valor de un ATT y lo asigna usando el BC. // Habra varios de estos.
Ademas de todos estos, se generaran (opcionalemente) los objetos que faciliten el acceso remoto a la API desde la misma KB.
CustomerRemoteInsert //Llama a traves de REST a CustomerInsert
CustomerRemoteDelete //
Esto puede hacerse para todos los servicios publicados en la API.
Acceso local (desde la KB, a la entidad, en forma local)
//Usa internal como protocolo &CustomerCollection = CustomerAPI.GetAll()
Lo ideal, seria poder escribir siempre el mismo codigo y por configuracion, poder decirle si es remoto o local
//Para poder llamar remoto (ESTO ES SOLO UNA SUGERENCIA) [Protocol=REST, host='remotehost', baseurl='\remotebaseurl'] &CustomerCollection = CustomerAPI.GetAll() //Para llamar al objeto local [Protocol =INTERNAL] &CustomerCollection = CustomerAPI.GetAll()
Cosas que faltan (definir)
Codigo de Retorno
Que deben devolver ? Alcanza con &RestCode ? Si se usa otro protocolo en el futuro, ese codigo de retorno va a alcanzar?
Estructura de Mensajes ? Debe depender del modulo GeneXus o ser independiente?
Usar una SDT unico de retorno, que incluya mensajes (id y message) y codigo de retorno?
Como probar el objeto API ? (ESTA ES IMPORTANTE)
Hay que definir bien como se puede testear en forma unitaria los objetos API y aseguarse que sean compatibles y no se rompa la compatiblidad sobre todo de los objetos API que sean publicados para ser consumidos desde fuera de la aplicacion.
Como hacer el Deploy de los objetos API ?
Pensar en la necesidad de publicar en API Gateways y softwares parecidos.
Clave de la entidad.
Cuando la clave de la entidad es compuesta, uso un SDT con los atributos y paso como parametro el SDT o conviene usar variables basadas en atributos que la componen?SDT tiene la ventaja que queda facil de definir, y la llamada queda con menos parametros y mas facil de leer.
La desventaja es que al llamarlo, tengo que cargar el SDT
ATT tiene la ventaja que no debo cargar nada para llamar al objeto
La desventaja es ques la entidad tiene muchos atributos en la clave, es dificil de entender en los parametros posicionales.
Versionado
El nro de version, se puede manejar (inicialmente y de forma simple) en la propiedad Service Base Path del objeto API
En una segunda version se podra definir diferente.
Service Base Path = <API object name>/v1/
Log
Falta definir que nivel de log se va a llevar y que eventos/errores se van a registrar
Seguridad
Falta definir cómo se van a asegurar estos servicios.
Una cosa importante es poder separar las API como internas (que solo puedan acceder desde dentro de la KB que las contienen) y externas (que puedan ser accedidas como servicios).
Throttling (Control de ancho de banda y cantidad de invocaciones)
Control de no abuso de estos servicios. Poder establecer algunas reglas de control para determinar si alguien esta abusando o haciendo un DoS y no ejecutarlo.
After / Before
Definir la mejor forma de programar los eventos After y Before de cada servicio y los globales.
Paginado en los Get que devuelven Collection
En los data provider que devuelvan collection, agregar paginado.
Luego podemos tener 2 metodos, uno con paginado y otro sin paginado, que utilicen al mismo data provider, para simplificar la programacion en caso que quiera traer todo.
Agregacion
Ver si se pueden generar en forma mas o menos facil, funciones de agregacion por un conjunto de atributos.
Elegir los indicadores y una funcion de agregacion SUM, AVG, MIN, MAX, COUNT
Elegir por que dimensiones se quiere agrupar y devolver los registros totalizadores de eso.
Comentarios
Publicar un comentario
1) Lee el post
2) Poné tu opinión sobre el mismo.
Todos los comentarios serán leidos y la mayoría son publicados.