El Guardián de las Crypto - visualizador web de precios de criptomonedas

Kaledros

Y su puta madre para encontrar un nombre con "crypto" o "crypt" que no estuviera pillado ya y me gustara.

Motivación

Llenar portafolio y aprender, básicamente. Quiero ver si soy capaz de crear yo solo una webapp completa, funcional y profesional que no parezca el producto de un cursillo de Java. Con su backend separado del frontend, persistencia en BD, dockerización, despliegue y mantenimiento (y posible ampliación). Estoy podrido de trabajar con proyectos así, pero nunca he hecho uno yo solo desde cero (aunque sea así de simple). Hay cosas que sé hacer, cosas que no hecho nunca y cosas que no sé ni por dónde empezar a mirar, así que me vendrá bien para aprender.

Explicación de la falla

El proyecto consta de una web para visualizar el precio de varias criptomonedas y del servicio de backend al que consulta para ello.

La cosa es sencilla: un servicio que recibe peticiones (en este caso peticiones AJAX de un front web, pero puede recibir de cualquiera a través de un endpoint), hace llamadas a la API de un servicio de terceros que devuelve la información, persiste los resultados en un Mongo y tiene una task que cada una vez al día manda un correo a una dirección con el precio de las monedas en ese momento (y tal vez algo más, no lo tengo claro aún). Aparte de eso, tiene otra task que cada sesenta minutos hace una llamada a la API y persiste los resultados para confeccionar un histórico de precios de cada moneda.

El front tendrá versión desktop y mobile y además de mostrar el precio de las monedas cada vez que se entre el usuario podrá realizar cuando quiera un refresco de precios por moneda y en total, enviando otra petición al servidor, y mostrará el historial de cada moneda. Esto último tengo que ver como lo hago, tal vez me monte una gráfica o algo así. Para el diseño del front me inspiraré en Coinbase, que me mola mucho lo limpio y minimalista que es sobre todo en mobile. Igual le doy una vuelta de tuerca y lo hago más terror cartoon para que vaya acorde con el nombre, en cuyo caso le pediré ayuda a mi novia que es diseñadora gráfica. El front iría en su repositorio por separado, claro.

La idea es que el servicio sea escalable si algún día me apetece, porque la API que uso para consultar precios tiene más llamadas, con lo que la estructura seguirá un estándar que permita escalabilidad. También tiene que recuperarse de errores y ser todo lo robusto posible. No quiero ver 404 ni excepciones no capturadas.

El Trello con las tareas está incompleto, voy añadiendo y ampliando las existentes conforme se me ocurren.

Restricciones

Que va a ser todo gratuito de momento, desde la instancia en la que la suba hasta el usuario que uso para la API de precios. Tal vez acabe pasando por caja para extender el tier de la API de consulta de precios, porque si tiene mucho tráfico me quedaré sin transacciones, que en el tier gratuito son pocas, y la web no funcionará. Si llega el momento en que es viable planteármelo no me dolería rascarme el bolsillo, señal de que la web tiene tráfico.

Tecnologías

  • Java para el backend.
  • HTML, CSS, JS para el frontend.
  • MongoDB para persistencia.
  • Docker y Kubernetes.
  • Google Cloud (o similar siempre que tenga tier gratuito.

Herramientas

  • IntelliJ IDEA
  • Visual Studio Code
  • Postman
  • Gitkraken
  • Trello
  • Spotify con una playlist de grunge de los 90

Tareas, sin orden concreto

  • Creación de la app y prueba de concepto
  • Tareas automatizadas
  • Capa de persistencia en MongoDB
  • Cacheado (opcional)
  • Historial de precios
  • Envío de correo automático
  • Creación del frontend
  • Dockerización
  • Despliegue
  • Documentación + proposición de mejoras

Como digo, está incompleto y es ampliable. Por ejemplo "creación del frontend" no es una sola tarea, claro.

Tareas futuribles por si algún día me apetece

  • Portar la aplicación a Spring Boot
  • Usar algún framework de JS para el front
  • Añadir funcionalidades a la página
10
Kaledros

Día 1

Ayer, realmente.

Pues el primer día ha consistido básicamente en análisis y diseño, esquematización de la aplicación, elegir las tecnologías y desglosar las tareas. Luego he creado el backend casi como una PoC, para asegurarme de que podía recibir peticiones, procesarlas y recuperar y procesar las respuestas. Lo hace sin problemas, pero aún no hay ningún tipo de gestión de errores ni de entrada ni de salida, ni mucho menos durante el procesado de datos. Esa será la siguiente tarea.

GaaRa90

mave/gradle, jira (si utilizas trello para ello, jira me gusta mas pero no he probado su version free) & confluence.

posiblemente en el futuro si se te alarga y trabajas, te venga bien algun sitio donde documentar.

Saludos.

Edit: no me he leido el post entero, por si he omitido algo importante.

1 1 respuesta
Kaledros

#3 Gracias por el feedback :)

Sí, uso Maven para el proyecto. La verdad es que ni se me ha ocurrido mirar Jira, he asumido que no tendría tier gratuito o que estaría muy restringido y ni lo he visto. Igual luego lo miro y si me convence migro.

La documentación aún no he decidido dónde hacerla, igual me hago un documento en markdown que me mola como queda.

Kaledros

Día 2

  • Añadido logger en todas las clases.
  • Añadidos unos cuantos tests más incluyendo captura de excepciones.
  • Añadido control de excepciones y recuperación de errores.

Hoy toca empezar a diseñar el front y en función de lo que quiera mostrar analizaré lo que tengo que enviar y cómo procesar la respuesta.

B

Me has inspirado tio.

Tengo muy claro que mi próximo curro quiero que sea en una empresa de crypto.

No tengo claro si hacer lo mismo que tú. A mi me gustaría programar crypto de verdad, interactuar con la blockchain y/o wallets. Lo iré pensando...

Nunca he hecho un side project en mi tiempo libre, pero creo que va siendo hora. Tenía ésta semana de vacaciones y no he hecho casi nada xd. A ver si me voy animando poco a poco.

Ánimos, te voy siguiendo. Si te haces diagramas y tal, estaría guay que los postearas tambien.

1
Ranthas

No es un tema que me apasione, pero tienes manita y cuando quieras meterle mano a Spring, cuenta conmigo.

Para el front, si te decides por Angular, también puedo echarte una mano.

Ánimo y no dejes el proyecto a medias, que aquí no somos como los amigos de @Jastro

5 1 respuesta
Kaledros

#7 Pues estoy ahora con el front y me estoy pegando con CSS como un imbécil XD Mi idea es clonar directamente esta UI de coinbase:

Estoy intentando hacer lo de las filas en plan mockup (metiendo valores a mano y repitiéndolos en cada una) y no tengo narices. Inspeccionando el código veo que son todo tablas, que es lo que me imaginaba, pero estoy pegándome con flexbox y no hay manera. Y tampoco quiero rendirme al cabo de un solo día y tirar por un framework, la verdad.

1 respuesta
Kaledros

Sorry por el doblepost, pero he dado por finalizado el día.

Día 3

Lo he intentado con el frontend. De momento me ha ganado. Mañana más.

En la parte back he reorganizado el proyecto, he creado interfaces y sus implementaciones, movido cosas, añadido un par de campos y en general dejándolo todo más escalable y legible. No he cerrado release todavía aunque podría hacerlo perfectamente, me espero por si se me van ocurriendo cosas.

Y por cierto, la combinación del theme Dracula y la fuente JetBrains Mono me mola cada día más. La tengo en el IDE y en el VSC

1 respuesta
Ranthas

#8 Mi recomendación:

Pasa del maquetado de front-end. Yo lo hago siempre. Centrate en la funcionalidad, organización del código, componentes, etc y ya por último, el maquetado (poner todo bonito, en su sitio).

De esta manera reduces el tiempo que pierdes y lo redistribuyes en tareas donde eres más productivo, perdiendo menos concentración.

PD: servlets, que dolor

1 respuesta
Kaledros
#10Ranthas:

servlets, que dolor

¿Cómo hubieras montado tú un servicio rest sin framework? No se me ocurría nada mejor sin usar Spring, la verdad.

Edit: y tienes toda la razón, mañana empezaré a organizar componentes y paso de maquetar nada.

1 respuesta
Ranthas

#11 Pues no lo monto.

Llevo con Spring desde la versión 3 (año 2009), y sinceramente, hoy por hoy no contemplo otra opción. La cantidad de tiempo que ahorras es brutal.

1 respuesta
Kaledros

#12 Ya, si me encanta y soluciona mucho la vida, pero quería reforzar conocimientos del estándar de Java. Cuando lo refactorice tardaré una tarde XD

r2d2rigo

#9 yo que soy nulo con el frontend (y de hecho lo odio), me he acabado acostumbrando a Bootstrap. Usa algun framework como ese porque lo hace muy para tontos y aunque no tengas ni idea acabas siendo productivo.

1 respuesta
Kaledros

#14 Por un lado no quiero usar nada más que CSS a pelo porque obviamente soy un n00b en front y quería aprender. Por otro, creo que lo más eficiente sería aprender React o cualquier otro framework front que tenga demanda (lo suficiente para hacer esto, no ser un experto en ello), añadirlo al CV y al portfolio con un proyecto terminado y que salga el sol por Antequera.

Y creo que es lo que voy a acabar haciendo...

1 respuesta
Kaledros

Día 4

No he avanzado en la aplicación, me he dedicado todo el día a estudiar cosas que tendré que hacer y que no sé hacer (MongoDB y Docker, básicamente). Si mañana va todo bien implementaré la capa de persistencia.

También es posible que mañana me contacten los de la entrevista para aclararme las dudas, decirme que no sigo en el proceso o directamente me ghosteen y pasen de mí. En cualquier caso tampoco me importa, una empresa que me pasa la prueba técnica errónea tras una primera entrevista y que tarda más de un día en responder (no son Google, precisamente) no me inspira confianza.

isvidal

#15 Yo te animaria a utilizar Bootstrap, te dará el empujoncito que necesitas y maquetar se convertirá en trivial una vez te sepas todos sus utils y sintaxis, no hace falta ni que uses sus componentes, sólo sus clases. col-md-6, row, etc...

Y si quieres entender sobre flexbox no hay mejor y más visual tutorial que este:

https://css-tricks.com/snippets/css/a-guide-to-flexbox/

Y aunque uses React sigues teniendo que saber maquetar el CSS!

1
Kaledros

Día 5

El día de los bugs deserializando la respuesta. Es lo que tiene usar una API que no está muy bien documentada.

Primero he tenido que resolver un bug en el que yo había supuesto que un miembro de una clase era un String porque siempre me venía a null. Pues no, era un objeto, hasta que se me ha ocurrido mirar en el json de la respuesta.

El segundo todavía no lo he solucionado. El tema es que pierdo un objeto anidado por el camino. La respuesta del servicio es tal que esta:

"data":[ {
        "id":1,
        "name":"Bitcoin",
        "symbol":"BTC",
        "slug":"bitcoin",
        "num_market_pairs":7997,
        "date_added":"2013-04-28T00:00:00.000Z",
        "tags":["mineable"],
        "max_supply":21000000,
        "circulating_supply":18353512,
        "total_supply":18353512,
        "platform":null,
        "cmc_rank":1,
        "last_updated":"2020-04-30T14:18:32.000Z",
        "quote": {
            "USD": {
                "price": 8849.96060035, "volume_24h": 72540472164.7273, "percent_change_1h": 0.26688, "percent_change_24h": 6.46298, "percent_change_7d": 21.501, "market_cap": 162427858078.0509, "last_updated": "2020-04-30T14:18:32.000Z"
            }
        }
    }, 
//más entradas en el array "data"
]

Y para eso tengo varios objetos. Un objeto CryptoResponseData, que es el que mapea cada entrada del array "data", un objeto "Quote" para mapear el objeto "quote" y uno "Currency" para mapear lo que en el ejemplo viene con el nombre "USD", que cambia según la moneda que envíes en la request:

public class CryptoResponseData{

private String id;
private String name;
private String symbol;
private String slug;
private String num_market_pairs;
private Date date_added;
private List<String> tags;
private long circulating_supply;
private long total_supply;
private Platform platform;
private String cmc_rank;
private Date last_updated;
private Quote quote;
}

public class Quote{

private Currency currency;
}

public class Currency{

private double price;
private double volume_24h;
private double precent_change_1h;
private double precent_change_24h;
private double precent_change_7d;
private double market_cap;
private Date last_updated;
}

Y el código que mapea la response es el siguiente:

    JsonObject element = new Gson().fromJson(str, JsonObject.class); //"str" es el json
    JsonElement dataWrapper = element.get("data");
    JsonElement statusWrapper = element.get("status"); //Hasta aquí, todo bien
    CryptoResponseData[] response = new Gson().fromJson(dataWrapper,CryptoResponseData[].class); //Aquí he perdido la Currency

Estoy seguro que estoy mapeando mal la currency, posiblemente porque en ningún sitio he metido ese "USD", pero no se me ocurre qué puede ser. En fin, mañana más.

Edit. Arreglado, el problema era que Quote no es un objeto, es un mapa de currencies. Era cambiar esto:

private Quote quote;

Por esto:

private Map<String, Currency> quote;

Y cargarme la clase Quote, claro.

HeXaN

¿Estás usando JAVA porque es lo que conoces? Hubiese molado tirar a por Kotlin, por ejemplo.

1 respuesta
Kaledros

#19 Y porque es para portafolio y de momento Kotlin no lo contemplo. Pero cuando tenga curro estable sí que quiero probar algún lenguaje compatible con la JVM.

nobody1

Mucho ánimo, con dos cojones, no lo dejes como hacemos el resto.
Yo empecé una movida en octubre, hice el MVP y no lo volví a tocar... No seas como yo y acábalo!!

1
Postmortem

Ánimo con el proyecto, casualmente yo también estoy trabajando en un proyecto web con Java/Spring y Angular, si tienes cualquier duda coméntame y probablemente te pueda decir algo rápido.

Pronto creo que haré un post de diario de desarrollo.

1
Kaledros

Día... a ver que mire... 6

Hoy no he podido adelantar nada, he estado todo el día liado en casa y fuera y no he podido sentarme ni cinco minutos seguidos a darle al proyecto. Mañana más, toca seguir con la capa de persistencia.

Kaledros

Día 7

Hoy y mañana no voy a hacer ni el huevo que es ya una semana entera currando y no me pagan para esto, así que a descansar. Por otra parte, el proyecto necesariamente va a ir más lento a partir de ahora porque he llegado al límite de lo que puedo hacer sin aprender nada nuevo, así que me tomaré los próximos días para estudiar cosas.

2
Kaledros

Día 8

No tengo remedio, al final sí he trabajado en el proyecto porque podía ir adelantando cosas. La capa de persistencia está acabada a falta de un par de detalles que puedan ir surgiendo.

He creado dos DB, una para producción y una para test. Quiero tener completamente separados ambos flujos, nada de dos colecciones en la misma DB. De momento he creado una colección para guardar cotizaciones, que son básicamente los datos que se devuelven al front y que se pintan.

El flujo, por ahora, es tal que:

  • El front (de momento el Postman, pero bueno) envía una petición POST con una payload.
  • El servlet la recibe y dependiendo del servicio que se solicite la enruta por un sitio diferente. De momento sólo hay uno, pero ya está modularizado para que pueda escalar en el futuro.
  • El servlet llama a un controller.
  • El controller llama a un servicio. Ese servicio hace varias cosas. Por un lado, llama a la API que devuelve los datos de cotización. Por otro, cuando ya tiene la respuesta la mapea a POJOs, crea objetos de dominio a partir de esos POJOs y los envía a un repository. Y por último devuelve la respuesta al frontend. La respuesta contiene los objetos de modelo serializados a json.
  • Ese repository implementa una interfaz mongoRepository con un solo método (de momento), saveAll(), y lo implementa. Mapea los objetos de dominio a documents y los persiste.

Llamada del Postman:

Ejecutando un find() en mongo tras esa misma llamada:

Siguientes pasos

  • Separar el flujo de persistencia del de retorno de datos. Ahora mismo la aplicación no devuelve los datos al front hasta que se han persistido, y eso retrasa mucho el envío de información y por tanto el pintado de datos en la web. Puedo arreglarlo con una caché o puedo separar ambos flujos con threading. Después de pensarlo, me decanto por el threading. La caché es fácil de implementar y queda muy aparente en el proyecto, pero implicaría que los datos se quedarían congelados durante una hora, o el intervalo que le ponga, y me parece una chapuza y un sinsentido tener una app de consulta de precios que no muestre información en tiempo real. Por otro lado, el threading elimina la posibilidad de que las respuestas tarden más de lo que cuesta procesarlas pero no he tocado nunca nada de concurrencia porque todos los frameworks que he usado se encargan de él por debajo sin que el programador tenga que tocar nada.
  • Diseñar la llamada de histórico. Esta es una llamada que un scheduler realizará cada hora y popula una collection en la que sólo están los datos de los precios de las criptomonedas y sus cambios de moneda correspondientes. Quiero tener como mínimo el cambio de moneda en EUR y USD y el histórico por horas, días, semanas, meses y años. Obviamente al principio no tendré ni siquiera semanas, tendré que controlar que si no hay datos anteriores a una fecha dada en un intervalo concreto devuelva un mensaje.

Edit: mirando bien la respuesta del servicio voy a tener que trimear esos double para ponerlos bonitos, no quiero que el front haga ningún cálculo ni nada, que reciba los datos y los pinte tal cual.

B

Es cosa mia o no necesitas backend?

1 respuesta
Kaledros

#26 Podría hacerlo todo en front, sí, con llamar a la API y procesar la respuesta en el propio front con JS sería suficiente.

1
Ranthas

De todas maneras está bien tener un backend, supongo que más adelante puede haber gestión de usuarios, podrás organizar tu cartera de criptomonedas (porque no te interesa seguir todas las que te ofrece la API), o incluso más interesante, podría haber un apartado que te calcule a partir de los históricos la evolución de la moneda e intentar calcular la tendencia a 1-3-6 meses vista.

1
Kaledros

Exacto, la idea es que se pueda ampliar si se quiere en el futuro y usar la API simplemente como fuente de datos, como si fuera una consulta a DB pero usando un servicio externo.

Kaledros

Día 9

Hoy ha tocado cerrar release (0.2) y estudiar. Y efectivamente, en el tema front he estado haciendo el mongolo porque Bootstrap te lo hace todo. Ahora vendrá algún gurú de frontend diciendo que eso es antediluviano, pero si me sirve me sobra.

Mañana seguiré con Bootstrap, voy a olvidarme unos días del back para no agobiarme.