Hace unas semanas descubrí que Karina tenía razón hace 20 años, solo hizo falta que esos 20 años pasaran, que yo abriera mi mente y me animara a más. A modo de reconocimiento y por si ayuda a algún otro a abrir su mente, va este post.
Mi problema en ese momento
Hace muchos años que trabajo con GeneXus, empecé en un cliente hace como 20 años y recuerdo una llamada a soporte, en ese momento Karina era "soporte-documentación-test-capacitación-consultoría-y-aindamais".
En ese momento yo estaba desarrollando un sistema para el control de flotas (stock de repuestos, manejo de combustible, manejo del taller, etc).
Precisaba hacer un proceso de actualización de stock "batch" pero GX en ese momento solo tenía Transacciones y Reportes. No existían los procedures (mucho menos workpanels ni nada de lo que existe hoy).
Llamé a soporte a ver como podía resolver el tema y Karina me intentó convencer de que lo que estaba haciendo estaba "conceptualmente mal", que la actualización a la base de datos debia ser vía la transacción que era donde estaban las reglas del negocio y aseguraba la consistencia de los datos, etc, etc. Argumentación atendible.
Yo tenía 20 años, era un pollito de Facultad y Karina era muy convincente. A pesar de ello no me convenció y yo pecador (y vasco) hice un programita en DBase III Plus (¡que producto!) muy sencillo e integrable que resolvió el tema, por suerte no hubo que migrar eso a RPG porque ahí hubiera patinado un poco más :)
Nota: Antes de seguir aclaro que este post no es un plagio al de Armin sobre el tema, coincidimos que a ambos nos dio por escribir sobre eso nomás y el publicó primero (mientras yo me peleaba con el Camtasia!!).
Mi problema hoy
Hace unos días tuve que hacer una aplicación y quería que tuviera Geografías (la típica de Pais/Estado/Ciudad) y tomé por el camino "conocido".
Lo primero que hice fue definir la estructura geográfica que quería, bastante sencilla:
CountryID*
CountryName
(StateId*
StateName
(CityId*
CityName))
Luego me puse a desarrollar por el "viejo_conocido_seguro_confiable" camino de un procedure con "news" que poblara los datos. Algo como:
new
CountryId=1
CountryName="Uruguay"
endnew
new
CountryId=1
StateId=1
StateName="Montevideo"
endnew
new
CountryId=1
StateId=1
CityId=1
CityName="Montevideo"
endnew
etc, etc, etc.
Estuve un rato de "copy/paste" bastante largo y aburrido, proceso en el cual cometí N errores (me suele suceder cuando me aburro) porque en un bloque ponía el código del país mal y me quedaban mal los datos y así sucesivamente.
Tiene que haber un modo mejor
Esto de "inicializar" datos es algo que suelo precisar porque hago muchas KBs "from scratch" y siempre el tener un juego de datos "razonable" es un problema.
Creo también que es algo bastante común en procesos de implantación donde luego del "create" hay que poblar con datos iniciales la base de datos.
Anduve buscando algunas KBs a ver como lo tenían resuelto (¡aguante GXserver!) y me encontré en varios lugares la misma solución del proc y los news. Incluso alguna parecida a lo que quería pero no lo suficiente para re-usarla a bajo costo.
Me acordé de un DataProvider que publiqué hace tiempo en el wiki, uno que hice con un programita que leia los ISO codes de un excel y generaba un TXT con el formato DP, luego "copié/pegué" del TXT en el DataProvider. A veces practico esos deportes :)
Ese está bueno pero no tiene los estados ni ciudades... en fin... no aplicaba mucho a mi caso, sin embargo por el lado de los DP parecía venir la solución.
Al final de la historia (así la hago corta) me quedé con una solución con DataProviders que que retorna los datos y un procedure que en lugar de un "new" usa un business component, también usé autonumber y un par de serial para sacarme de encima el lío de la primary key.
A continuación un video de cómo lo hice:
Ventajas que encontré
1. Más simple. Quiero poblar la estructura de Countries, entonces Drag&Drop de la misma a un DP y alguna cosita más y listo.
2. Más barato. Me llevó mucho menos tiempo la versión inicial. Si bien tuve un trabajo de "copy/paste" fue más sencillo, al menos no me preocupé para nada de los CountryCode, StateCode, etc.
3.Más claro. Separé el "alta" de la "fuente de datos", con esto si quiero agregar datos no tengo que meterme con los news ni nada, simplemente los agrego en el DP.
4. Más potente. En mi caso era una estructura bastante sencilla con reglas sencillas, pero igual me resolvió el alta, la numeración y las reglas que tuviera o vaya a tener asociadas al Country etc (si le tengo que agregar alguna regla a la TRN de Countries se que no tendré que modificar el procedure, esto es importante para la consistencia/calidad de los datos).
5. Más re-usable. Mi desafío ahora es evitarme el copy/paste e intuyo que via el Data Provider Generator y servicios como los de freesbase.com no tendré mayores inconvenientes en hacerlo (quedará para cuando pueda bucear en freebase.com un poco más).
En cualquier caso si esto lo fuera a implantar en un cliente talvez la fuente de datos sea otra y habiendo separado los datos del alta en si creo que será más fácil adaptarel "input" del DP.
En fin, unas cuantas ventajas del uso de DP y BC en un escenario como el descrito. Luego de "abrir mi mente" respecto a los DP y BC creo que hay muchos más escenarios de uso posibles para los mismos así que mi mensaje es solamente ese: mantenga su mente alerta, hay nuevas y mejores soluciones para nuevos y viejos problemas.
Ahh... me olvidaba: Karina, tenías razón, solo se trata de abrir la mente y para la actualización alcanza la transacción (o casi).
Mi problema en ese momento
Hace muchos años que trabajo con GeneXus, empecé en un cliente hace como 20 años y recuerdo una llamada a soporte, en ese momento Karina era "soporte-documentación-test-capacitación-consultoría-y-aindamais".
En ese momento yo estaba desarrollando un sistema para el control de flotas (stock de repuestos, manejo de combustible, manejo del taller, etc).
Precisaba hacer un proceso de actualización de stock "batch" pero GX en ese momento solo tenía Transacciones y Reportes. No existían los procedures (mucho menos workpanels ni nada de lo que existe hoy).
Llamé a soporte a ver como podía resolver el tema y Karina me intentó convencer de que lo que estaba haciendo estaba "conceptualmente mal", que la actualización a la base de datos debia ser vía la transacción que era donde estaban las reglas del negocio y aseguraba la consistencia de los datos, etc, etc. Argumentación atendible.
Yo tenía 20 años, era un pollito de Facultad y Karina era muy convincente. A pesar de ello no me convenció y yo pecador (y vasco) hice un programita en DBase III Plus (¡que producto!) muy sencillo e integrable que resolvió el tema, por suerte no hubo que migrar eso a RPG porque ahí hubiera patinado un poco más :)
Nota: Antes de seguir aclaro que este post no es un plagio al de Armin sobre el tema, coincidimos que a ambos nos dio por escribir sobre eso nomás y el publicó primero (mientras yo me peleaba con el Camtasia!!).
Mi problema hoy
Hace unos días tuve que hacer una aplicación y quería que tuviera Geografías (la típica de Pais/Estado/Ciudad) y tomé por el camino "conocido".
Lo primero que hice fue definir la estructura geográfica que quería, bastante sencilla:
CountryID*
CountryName
(StateId*
StateName
(CityId*
CityName))
Luego me puse a desarrollar por el "viejo_conocido_seguro_confiable" camino de un procedure con "news" que poblara los datos. Algo como:
new
CountryId=1
CountryName="Uruguay"
endnew
new
CountryId=1
StateId=1
StateName="Montevideo"
endnew
new
CountryId=1
StateId=1
CityId=1
CityName="Montevideo"
endnew
etc, etc, etc.
Estuve un rato de "copy/paste" bastante largo y aburrido, proceso en el cual cometí N errores (me suele suceder cuando me aburro) porque en un bloque ponía el código del país mal y me quedaban mal los datos y así sucesivamente.
Tiene que haber un modo mejor
Esto de "inicializar" datos es algo que suelo precisar porque hago muchas KBs "from scratch" y siempre el tener un juego de datos "razonable" es un problema.
Creo también que es algo bastante común en procesos de implantación donde luego del "create" hay que poblar con datos iniciales la base de datos.
Anduve buscando algunas KBs a ver como lo tenían resuelto (¡aguante GXserver!) y me encontré en varios lugares la misma solución del proc y los news. Incluso alguna parecida a lo que quería pero no lo suficiente para re-usarla a bajo costo.
Me acordé de un DataProvider que publiqué hace tiempo en el wiki, uno que hice con un programita que leia los ISO codes de un excel y generaba un TXT con el formato DP, luego "copié/pegué" del TXT en el DataProvider. A veces practico esos deportes :)
Ese está bueno pero no tiene los estados ni ciudades... en fin... no aplicaba mucho a mi caso, sin embargo por el lado de los DP parecía venir la solución.
Al final de la historia (así la hago corta) me quedé con una solución con DataProviders que que retorna los datos y un procedure que en lugar de un "new" usa un business component, también usé autonumber y un par de serial para sacarme de encima el lío de la primary key.
A continuación un video de cómo lo hice:
Ventajas que encontré
1. Más simple. Quiero poblar la estructura de Countries, entonces Drag&Drop de la misma a un DP y alguna cosita más y listo.
2. Más barato. Me llevó mucho menos tiempo la versión inicial. Si bien tuve un trabajo de "copy/paste" fue más sencillo, al menos no me preocupé para nada de los CountryCode, StateCode, etc.
3.Más claro. Separé el "alta" de la "fuente de datos", con esto si quiero agregar datos no tengo que meterme con los news ni nada, simplemente los agrego en el DP.
4. Más potente. En mi caso era una estructura bastante sencilla con reglas sencillas, pero igual me resolvió el alta, la numeración y las reglas que tuviera o vaya a tener asociadas al Country etc (si le tengo que agregar alguna regla a la TRN de Countries se que no tendré que modificar el procedure, esto es importante para la consistencia/calidad de los datos).
5. Más re-usable. Mi desafío ahora es evitarme el copy/paste e intuyo que via el Data Provider Generator y servicios como los de freesbase.com no tendré mayores inconvenientes en hacerlo (quedará para cuando pueda bucear en freebase.com un poco más).
En cualquier caso si esto lo fuera a implantar en un cliente talvez la fuente de datos sea otra y habiendo separado los datos del alta en si creo que será más fácil adaptarel "input" del DP.
En fin, unas cuantas ventajas del uso de DP y BC en un escenario como el descrito. Luego de "abrir mi mente" respecto a los DP y BC creo que hay muchos más escenarios de uso posibles para los mismos así que mi mensaje es solamente ese: mantenga su mente alerta, hay nuevas y mejores soluciones para nuevos y viejos problemas.
Ahh... me olvidaba: Karina, tenías razón, solo se trata de abrir la mente y para la actualización alcanza la transacción (o casi).
UPDATE: subi la KB a GXServer por si a alguno le interesa:
ResponderBorrarhttp://gxserver.genexusx.com/genexusserver/home.aspx?dp%20and%20bc%20sample
Muy bueno. El tema de los DP me gustò cuando lo empecè a entender, mezclarlo con BC, mejor aùn.
ResponderBorrarJorge
Muy instructivo, efectivamente es el primer paso para eliminar los datos "hardcoded"... lo que me ha venido a la mente, especialmente en el ejemplo que utilizas, es el hecho de que, aún utilizando una técnica mas limpia, se sigue utilizando la técnica de tener una serie de datos hardcoded en el programa... ¿no tendría sentido en este ejemplo el dar el paso adicional de hacer que todos los datos que se alimentan automáticamente estén codificados en archivos (xml seguramente seria lo mejor), separados del código, de modo que la generacion de los datos en la base de datos no dependiera directamente del código, y fuera personalizable por implementación (imagina que tienes que vender el software a un cliente de Brasil y otro en Uruguay, o en cualquier parte del mundo, la diferencia sería el "dame la estructura de tu país, que te hago una versión nueva del programa" contra "aquí tienes el xml de la estructura del país, añade lo que estimes oportuno, y luego lo cargamos"...
ResponderBorrarEn realidad lo comentas de pasada en un momento del vídeo, pero creo que sería interesante el eliminar definitivamente la costumbre de dejar valores fijados en el código.
Jesus,
ResponderBorrarComparto lo que decis, diría que el desarrollo de los "data providers", el data provider generator y algunas otras funcionalidades que están en I&D van por ese lado.
De todos modos mi objetivo primario ha sido separar la lógica del "insert" de los datos en si. Lo que me complicaba era tener ambas cosas mezcladas porque perdía dinamismo, además como no usaba BCs se me complicaba la integridad, re-uso de reglas, etc.
Me ha tocado participar en algunos proyectos y las "fuentes de datos" son bastante diversas, diría que TXT, DBFs, XML, un export de SQL, XLS, WS, etc.
En mi opinión, en el caso que se tienen esas fuentes tan "diversas", se puede hacer un procedure (script, XSL, etc) que lo transforme en el SDT que el procedure que inserta utilice.
O sea, el "input" del procedure debería ser siempre el mismo, en mi caso me servía un DP, en tu caso diría que en lugar de invocar al DP podrías hacer un procedure que lea el XML y le pase eso como SDT al procedure del insert.
¿Podrás en todos los casos zafar de la programacion de ese procedure que produce el "output" que el otro precisa como "input"?
Estimo que no (por la "diversidad" de las fuentes) pero por lo menos te asegurás que el insert quede bien y no precises nunca re-programarlo.
Si en tu caso logras un XML con la estructura geográfica sería entonces hacer un proc que lea el XML y le pase el SDT al procedure que hace el insert.
NOTAS:
1. Habrá más post/news sobre el tema DP especificamente.
2. El tema de la "inicialización" de datos es divertido y diría que por el lado del "initial value" puede que haya novedades :)
Romper paradigmas crecer.
ResponderBorrarpero segun mis calclulos 19 años.
Un abrazo
Si, pueden ser 19 pero para los que pasamos los 40 ya los números "exactos" no son tan importantes :)
ResponderBorrarMuy buena info, te hago una consulta, estoy haciendo un proyecto y me surge la siguiente duda, se trata de un banco, este al crear un cliente de enmediato tendria q crearle una tarjeta de credito, como tendria q hacerlo, ya q en el video vos le das los nombres del pais, etc, y yo lo que qeuiro es q el usuario ignrese los datos del cliente y ahi se cree la tarjeta
ResponderBorrargracias
Lucho,
ResponderBorrarDepende un poco como modeles la realidad.
Podria ser algo asi:
OPCION 1 - todo en una TRN.
Definis una transacción de dos niveles, algo como:
ClienteId*
ClienteNombre
ClienteCantTarjetas=count(TarjetaFchVto)
(TarjetaNro*
TarjetaFchVto)
Para asegurarte que ingrese por lo menos una tarjeta podrias poner algo como:
Error("debe ingresar por lo menos una tarjeta") if ClienteCantTarjetas=0 on after(level(TarjetaFchVto)
Nota: la sintaxis puede no se exacta
OPCION 2: dos transacciones "en cadena"
Definis una transacción de clientes:
ClienteId*
ClienteNombre
Una de tarjetas:
ClienteId*
TarjetaNro*
TarjetaFchVto
En la primera pones un:
tarjetas.call(ClienteId) on afterInsert
Depende como quieras la operativa en la de Tarjetas podes poner un error si quiere salir de esa TRN sin dar de alta una tarjeta o en la de Clientes borrar el cliente si no ingresó una tarjeta.
Hay muchas maneras más de implementarlo, depende un poco de la realidad y lo que precises, incluso podría ser con BCs pero parecen más complicadas que las TRNs que describo