Diario sobre app web de digitalización de menús (Java + Angular + Mongo)

bornex

Buenas chavales/as!

Me gustaría compartir con vosotros un diario de desarrollo de una app que estamos haciendo un colega y yo en nuestros ratos libres (después del curro) y que ya hemos empezado. El proyecto es completamente open source y por ahora no tenemos ninguna intención de hacerlo close source o cualquier gitanada. Sin más os presento el proyecto.

* STACK *

Las tecnologías que hemos usado son las siguientes para el backend:

  • Java 14
  • Spring Boot 2.3.0

Para el frontend:

  • Angular 9

Infraestructura:

  • VPS en OVH con 2 vCPU
  • Mongo 4.2
  • Nginx como load balancer, también lo usamos para el SSL termination (con cert de Let's Encrypt of course)
  • GitHub actions para Continuous Integration, Continuous Delivery y Continuous Deployment con un poquito de bash.
  • Maven como build tool, con el generamos los artifacts deployeables
  • Docker solo para testing

* ARCH *

La arquitectura del proyecto es bien sencilla, una SPA servida por el backend de Spring en formato monolito (desplegando un único artifact), conectada con un MongoDB con el legacy driver (espero poder cambiar esto a reactivo en los próximos días). Todo montado en un único nodo y por ahora seguirá así ya que no creemos que esto crezca mucho.

Hemos querido seguir un approach hexagonal, aunque no estamos 100% desacoplados del framework, y estamos intentando seguir algunas reglas del DDD. Básicamente la idea principal que hemos querido implementar es que testeando los casos de uso a nivel unitario en el dominio, conseguimos que testear las otras capas sea más fácil y fiable en el caso de que queramos hacerlo con mocking.

Ahora mismo no usamos ningún tipo de approach orientado a eventos, aunque si tenemos planeado hacerlo en los próximos días.

* CI/CD *

El proceso de puesta en producción también es bastante secillo y rudimentario. Tenemos el frontend sin tests y sin linter, así que, de aquí lo único que puedo decir es que lo compilamos y lo movemos a la carpeta "resources/public" del backend para que Spring lo sirva de forma automática. En el backend tenemos unos pocos de tests unitarios y algunos de integración.

Los de integración los lanzamos contra la aplicación Spring con el contexto completamente levantado, un servidor web empotrado encendido y una base de datos en docker que es exactamente la misma versión de la base de datos que tenemos en prod.

Este proceso se ejecuta cada vez que se hace push a la rama master y esta pendiente su modificación para trabajar con merge-requests.

A grosso modo: compilamos frontend, se compila el backend, se lanzan los tests del backend y si todo va bien se hace el build de un artifact que se despliega en el servidor a traves de SSH y se aumenta la versión.

Por ahora no tenemos ningún tipo de job para hacer rollback, lo que significa que si metemos una cagada y el pipeline se ejecuta con éxito, meteremos la gamba en prod y habrá que hacer el rollback a manopla.

* MODEL *

El modelo también es bastante sencillo, por ahora lo único que tenemos es un documento root que es el "Customer" donde cuelgan los negocios y menús que pueda tener cada negocio. Un customer solo puede crear un menú, cambiar un menú y buscar un menú, además de hacerse una cuenta. Por simplicidad, ahora mismo sólo trabajamos con que un customer puede tener un único negocio y un único menú, pero la idea es extender el modelo para permitir múltiples negocios y múltiples menús por negocio.

* IDEA *

La idea es que los usuarios puedan crear cartas/menús y poder digitalizarlos y compartirlos en redes así no tendrán que tocarlos cuando vayan a un bar/restaurante debido al COVID-19.

* COSAS PENDIENTES *

  • Monitoring de logs <- tenemos pensado instalar ELK Stack o GrayLog, pero debido a nuestro servidor tan limitado instalar alguna de estas soluciones nos dejaria la aplicación sin iron.
  • Mejorar la seguridad de la app <- tenemos implementado el reCaptcha v3 de Google y JWT con Spring Security pero queremos meter paneles de admin y demás y securizarlos por Basic Auth o VPN.
  • Activar linter y tests en el frontend <- Por motivos de velocidad de desarrollo los hemos desactivado.
  • Hacer suit de UI tests con Cucumber y Selenium.

* REPOSITORIO *

Repo en GitHub

Cualquier duda o sugerencia bienvenida es ;) iré actualizando a diario para que veáis como va el desarrollo.

2
bornex

RESERVADO

kidandcat

No desactivéis los linters por nada, o dentro de un mes tendréis que echar todo abajo y empezar de nuevo
Por otro lado, la idea es muy buena, en mi empresa se esta valorando una muy parecida, pero vosotros dadle caña y aprovechad porque es un nicho no cubierto ahora mismo, y las empresas tienen que pasar unas negociaciones y burocracia que vosotros no.

1 respuesta
bornex

#3 Sí vaya, estaban desactivados más que nada por velocidad de desarrollo pero entre hoy y mañana los vuelvo a meter sin falta.

Por otro lado, la idea es buena y queremos validarla primero antes de seguir metiendole esfuerzo a esto, pagar el servidor y el dominio es un gasto asumible ya que los dos nos repartimos los gatos y estamos trabajando (no ERTE por ahora). Hay varias apps ya iguales, así que ya es quien tenga más visibilidad.

kidandcat
  • Para los logs podéis probar con https://sentry.io/, con el plan gratuito os debería sobrar, y no os mete carga en vuestra máquina porque es un SaaS.
  • Por otro lado, viendo el frontend, tenéis que darle una vueltecilla al menú. La letra roja sobre blanco/gris claro no se lee bien, además de que al usar los colores y el diseño vuestro corporativo, no parece una sección aparte de vuestra web (y debería serlo), creo que os sería fácil hacer que el usuario elija un color primario al crear el menú.
  • Mini tip de seguridad, tenéis que deshabilitar en Nginx que envíe las cabeceras de versiones, ya que eso facilita muchísimo orientar el vector de ataque a un site
    https://imgur.com/a/5RxyEWR
  • El CI os saldrá gratis, pero no lo uséis para testear cosas, en ningún proyecto vais a subir al CI más de 30 commits al día (así le dais caña a manejar git y setups en local).
  • Y por último (de momento :D) este tipo de proyectos personales es donde tenéis que darle caña a cosas nuevas, experimentar y, sobre todo, aprender un montón, así que os animo a aprender https://kotlinlang.org/ ya que es hacia donde se está moviendo el ecosistema JVM y debería encajar en vuestro proyecto sin problema ya que una de sus premisas es 100% compatibilidad e interoperabilidad con Java.

PD: Tiene muy buena pinta, aunque todavía os quede, mi más sincera enhorabuena!!

2 1 respuesta
bornex

#5 Buah tio, muchas gracias por todos los puntos, nos vienen de lujo.

18-Mayo-2020

  • Integrado Sentry para enviar los logs de ERROR y WARNING a su plataforma, este servicio además de recoger los logs nos manda un email de aviso del petardazo con el stacktrace y todo. Lo hemos integrado con el plugin que tienen de logback, muy muy sencillo.
  • Gracias a Sentry hemos descubierto un bug de un NPE en el reCaptcha que hemos arreglado.
  • Hemos quitado las cabeceras de Nginx que dicen la versión que tenemos instalada.
  • Añadido linter al frontend.
  • Creados algunos tickets para ir empezando a hacer tareas y trabajar en ramas.
1
JuAn4k4

Para linting no lo he probado pero vi el otro día esto https://www.sonarlint.org/intellij/ No se si autoformatea.
Y en el front, puedes meter prettier + husky con un precommit hook y te ahorras mucho curro de formateo

Yo usaría Docker para todo directamente

1 respuesta
bornex

#7 Ya lo tengo instalado :) muchas gracias por el aporte! En cuanto a lo de docker, sinceramente es más por perrería que otra cosa, pero si esto sigue adelante y tenemos que empezar a distribuir la aplicación seguramente empiece a sacar servicios y con contenedores seguramente se haría más fácil la tarea.

bornex

19-Mayo-2020

Hoy he estado trabajando en la integración de SendGrid para el envio de correos de confirmación cuando un usuario se registra. Hasta ahora no teníamos confirmación del email y con el trabajo de hoy (que no esta acabado) tenemos esta funcionalidad que nos evitará cuentas falsas y demás.
.
He querido utilizar una librería que le tenía ganas Vert.x y hacer el envío del email de forma asíncrona y por eventos. He integrado Spring Boot con Vert.x y cada vez que la aplicación arranca, deployeo los verticles necesarios (por ahora solo el del email). Debido a la naturaleza bloqueante de este verticle, lo estoy deployeando como un worker lo que hará que Vert.x le asigned un pool distinto y dejaré libre el EventLoop que utiliza por debajo.

Espero poder acabar el flujo de verificación del email mañana sin falta.

bornex

20-Mayo-2020

Bueno la verdad que hoy ha sido un día bastante movido. Vamos por partes:

  • Hemos recibido emails de algunos usuarios que estan ya usando la app y que necesitan ciertas features que estamos empezando a plantear sobre la mesa. Una de ellas es la de integrar un OCR para que los dueños de los restaurantes y bares puedan subir un pdf o una foto de sus cartas y las digitalicemos. Esto por ahora tenemos que darle un vuelta pero he estado investigando y parece que hay una librería que hace justo lo que queremos: https://github.com/tesseract-ocr/tesseract que tiene un port a Java, no se si alguien en la sala conoce alguna otra alternativa o solución para esto, pero sería una feature bastante interesante de tener.

  • Otra de las features que hemos recibido es la de poder hacer los menús en múltiples idiomas, esto a priori es fácil, simplemente darle la opción al usuario de poder hacer más de uno y que lo haga en el idioma que quiera y luego al usuario que ve el menú pues que pueda seleccionar el idioma en el que quiere verlo.

  • También hemos recibido correos de gente queriendo ayudar en traducir la app (una chica traductora se ha ofrecido), así que nos viene de lujo para poder darle una vuelta a los textos que tenemos en la app y dejarlos finos.

  • Estamos también viendo las posibles soluciones que podemos implementar para guardar las imágenes de los platos de los menús, si bien todavía no hemos empezado con este desarrollo, sería buena idea tener un third party y no llenar el servidor con imágenes ya que nos costaría espacio en disco y tenemos poco.

  • Hemos empezado con la implementación de los alérgenos que por ahora era totalmente dummy, seguramente se quede terminada para mañana. La hemos planteado hacer a nivel de frontend, es decir, como los alérgenos son estáticos (14 en total), hemos pensado meterlos en un JSON en el frontend y que me mande el nombre y un id inventado al backend a la hora de crear un plato con los alérgenos. También tenemos que tener en cuenta las traducciones.

  • Estoy acabando la verificación del correo y estoy aprendiendo Mongo, Spring Data y Mockito que no los había usado nunca. Una de las cosas que he decidido a la hora de guardar fechas en la base de datos es usar Instant de Java, y ya si en un futuro esto se internazionaliza a la hora de devolverlo al frontend se podría convertir al TimeZone que quisieramos.

  • Otra de las cosas que tengo que mirar es como hacer las migraciones de los datos en Mongo, cosa que no tengo ni idea y que estoy viendo librerías que ya ni se mantiene. ¿Alguien tiene idea de como va esto en Mongo?

Como datos curiosos ya tenemos unos 30 usuarios registrados y unos 27 menús creados, la verdad es que no esperábamos tener gente en tan poco tiempo pero parece que esto va para adelante.

Para mañana

Mañana tiene que estar si o si la verificación de email funcionando, tengo que saber como migrar las cuentas registradas ya para darlas como confirmadas (y que los usuarios ya existentes puedan acceder a la aplicación) y como meter el campo de "created" y "updated" a las colecciones de mongo que tenemos (migraciones como ya he comentado).

A nivel de frontend también queremos meter para mañana y pasado todo el tema de la página de confirmación del email y todos los mensajes de éxito o error a la hora de verificar los correos.

2 3 respuestas
HeXaN

#10 Tesseract (con el motor basado en LSTM) es la mejor herramienta del panorama actual.

2 respuestas
bornex

#11 vale pues entonces tiraremos por ahí, vaya es la que me han recomendado así que me alegra saber que vamos por el mismo camino. Sabes si hay dataset gordos para entrenar la red? He visto que esta gente ofrecen el suyo para distintos idiomas pero no sé cómo irá.

1 respuesta
HeXaN

#12 Aquí: https://github.com/tesseract-ocr/tessdata_best

1
Ranthas

#10 Yo he trabajado con Tesseract, más en concreto para reconocer el texto que los radares agregan a las fotos qu hacen, y la verdad es que no tiene comparación.

Lo único, aunque ya te lo imaginarás, es que las fotos deben tener bastante calidad, sobre todo si las fuentes son pequeñas.

Unrack

#11 No me había enterado que habían implementado lstm para usar los caracteres anteriores. Está bastante potente.

soulsville

Interesante. Gracias por compartir el proyecto, no había visto muchos casos de arquitecturas distintas de las típicas en un proyecto 'real' y por desgracia/suerte en trabajo solo se huele MVC.

Me sorprende que digas #10 que no habías tocado Mockito ni demás. ¿Todos esos tests los has hecho de buenas a primeras?

1 respuesta
bornex

21-Mayo-2020

Hoy hemos tenido varios bugazos en producción que nos han obligado a centrarnos en ellos en vez de sacar features. Los bugs eran:

  • El CSS del footer para móviles pequeños estaba poniendose encima del botón del card de registro y no se podía pulsar, un usuario nos ha avisado por correo que no podía registrarse y a raíz de ahí hemos encontrado el problema.
  • Otro bug que también ha sido gracias al aviso de un usuario era que no se podían ver los menús, nginx estaba devolviendo un 502 y un pete de error de tamaño de cabeceras muy grandes, lo hemos arreglado y segía petando. Al final el problema era que el JSON que devolvíamos en una cookie desde el backend era demasiado grande y el navegador la borraba automáticamente.

En cuanto a las features que tenemos pendientes siguen en progreso y esperamos acabarlas mañana o pasado:

  • Los alérgenos ya se muestran a la hora de crear el menú y estan traducidos. Hay que hacer unas cuantas pruebas más y los tenemos listos.
  • La verificación de email no la hemos tocado pero si he aprendido a como hacer migraciones de mongo con Spring Data, un problema que a priori me tenía un poco mosca. Ahora podemos cambiar la base de datos cuantas veces queramos. A priori lo que tengo pensado hacer es ejecutar un callback cada vez que un repositorio cargue un documento de mongo de algún tipo, lo actualizo en memoria si tiene el formato antiguo a lo nuevo y lo devuelvo, es decir, que cuando use repositoryX.findById(T whatever) me va a dar siempre el objeto correcto, trabajo con él y luego si lo persisto se actualiza solo. Esto de forma lazy me va a ir actualizando solo los documentos que se usen en la aplicación y va a dejar aquellos que no se busquen.

Para mañana

  • Alérgenos.
  • Verificación de emal (backend)

#16 Me alegro que te guste el proyecto, poco a poco iré probando cosas nuevas así que estate atento ;). En cuanto a lo de mockito es totalmente cierto, lo que pasa que si he trabajado con Spock con groovy y en algunos aspectos se parece, quiero darle más caña al tema del testing sobretodo al paquete de tests de aceptación que quiero usar Cucumber y Selenium. Y respondiendo a tu pregunta, sí, los he hecho pero obviamente buscando info en la internet. Al final mi idea (como ya comenté) es testear los casos de uso a nivel unitario y luego cubrir algunos casos con test de integración con servidor empotrado arrancado y base de datos en docker.

1
Soltrac

Me dedico a este mundo, desarrollo software de TPVs de hostelería y te voy a dar ideas de lo que necesitas.

Una API, es básico, tener conectado un TPV con una carta es algo que agradecer para sitios que cambian de carta cada 2 x 3. Muchos sitios de cartas online no disponen de esto y es una absurdez para los desarrolladores de TPVs que no tienen carta digital propia.

Ingredientes. Hay artículos tipo pizza que tienen ingredientes. Poder verlos con su precio.

Menús. Hay artículos que son menús como el menú del día. Se componen generalmente de un PRIMERO, un SEGUNDO y un POSTRE. Ojo, algunos platos pueden incrementar el precio del menú.

Porciones. Hay artículos que se venden por diferentes tipos. Ración, media ración, etc. Cada una con su precio.

Hay más cosas, ya dependiendo de la idiosincrasia del negocio, pero lo de arriba es básico par la mayoría.

Un saludo!

1 3 respuestas
bornex

#18 Muchas gracias tio! A que te refieres con lo de tener conectado el TPV, te refieres a integrar Redsys? y que los restaurantes cobren a través de ahí o como? Joder apuntamos todo lo que nos has dicho y nos ponemos manos a la obra

1 respuesta
Soltrac

#19 TPV es el software q usan en el restaurante. Teniendo tú una API, los desarrolladores de software de TPVs pueden subir los artículos a tu web automáticamente y así si cambian un artículo en el software, se cambia automáticamente en tu web.

Busca en google Software TPV Hostelería, somos muchos en España, ya no te digo en el mundo entero.

bornex

22-Mayo-2020

Sigo liado con la verificación por correo electrónico, estoy apunto de acabar pero he tenido que hacer mil cosas para que no me reviente la base de datos con los usuario antiguos (migraciones, migraciones everywhere). He conseguido testear de forma manual todo el flow para un usuario ya existente gracias a un dump de la base de datos de producción que me he bajado y uso en mi docker local para testear, pero tengo que pensar como cubrirlo con tests de integración, ya que no me fio, he pensado en tener una serie de métodos factory en las clases llamadas "XMigration" que me den un objecto Document con la versión antigua del a partir de un objeto nuevo.

Los alérgenos también nos estan suponiendo un problemilla sobretodo a nivel de frontend, pero bueno también van por buen camino.

Una vez hagamos release de estas dos cosas que espero que no se retrase más de mañana o pasado, empezaremos con el desarrollo de las cartas en varios idiomas y algunas de las propuestas de #18, seguramente la de la pizza y las porciones, ya que la API para integrar TPVs no se muy bien como abordarlo y la verdad es que a priori me parece bastante curro (aunque ya tenemos una API) tenemos que darle una vuelta.

Para mañana

  • Acabar verificación por correo electrónico.
  • Acabar alérgenos.
bornex

24-Mayo-2020

Bueno por fin hemos acabado el desarrollo de la verificación por correo electrónico y los alérgenos estan funcionando. Ahora tengo que ver que será lo próximo pero seguramente sean mejoras de la UI y el tema del OCR pero tengo que discutirlo con mi colega.

Hay una cosa que me esta dejando un poco loco: Cuando los usuarios se registran y reciben en su correo el link para confirmar el correo, el controlador que se encarga de gestionar ese GET se esta ejecutando entre 2 y 3 veces. He leído por SO que puede ser o por el navegador (por extensiones que tenga el usuario instaladas). No me convence mucho pero bueno, por ahora funciona y seguiré palante, espero que no de problemas.

Testeando he tenido algunos problemas con la asincronía de los eventos y tenía un par de flaky tests que los he arreglado (con un do-while puerco), he mirado las opciones que tengo para esto y hay una librería que se llama awaitability que cubre justo los problemas que estoy teniendo pero por no meter más dependencies lo he dejado como está.

Para mañana

  • Empezaré con el tema del OCR para ver si es potable sacarlo en una semana o así de trabajo. Sino, me pondré con cosillas más fáciles.

Datos curiosos

Tenemos más de 120 restaurantes registrados, más de 80 menús creados y una media de 10-12 usuarios simultáneos, el pico más grande de sesiones en un día han sido algo más de 650, no paramos de recibir correos de ideas y preguntas por parte de los usuarios y la verdad es que estamos muy contentos.

1 respuesta
kidandcat

#22 El tema de las peticiones por el email no te preocupes, es porque los proveedores de correo hacen varias peticiones para pasarle el "antivirus" al link, de esa forma son capaces de enviarte el email a spam o incluso ponerte una alerta si el contenido es sospechoso.

1
bornex

Ayer se me hizo un poco tarde y al final no puse el resumen del día, ahí va.

25-Mayo-2020

Hoy hemos tenido unos cuantos bugs que quiero comentar amistosamente con vosotros, los bugs son los siguientes:

  • Llevo viendo logs de usuarios que no pueden acceder a la aplicación bastante tiempo y siempre he estado pensando de que eran bots atacando al formulario de login. Pues bien, hoy nos ha llegado un correo de un usuario que decía que se había registrado y que había verificado su cuenta sin problema y que no podía acceder a la aplicación, rápidamente he puesto más logs en el reCaptcha para que mostrara el email de la persona que esta intentando logearse y el score (puntuación del 0 al 1 que google nos da de ese usuario) y le he dicho al usuario que vuelva a probar. Para mi sorpresa estaba dando un 0.3 y yo tengo que solo puedes entrar en la aplicación a partir de un 0.8 (según la doc del reCaptcha de google un score de 1 es un humano y de ahí para abajo hasta 0 vas siendo más bot). Total me pongo a mirar por SO y por issues de GitHub del reCaptcha de Google y me entero de que la version 3 de éste (que es la que usamos nosotros) esta rotísima y que la IA de google que identifica usuarios y bots es un mojón que puedes saltartelo con cualquier script de selenium. Como solución he bajado a 0.3 el score necesario para entrar en la aplicación y vamos a implementar el reCaptcha v2 que es el que funciona bien. Estoy cabreado porque vamos a tener que retrasar algunas features por culpa de esto, pero bueno...

  • Otra de las cosas que nos han pasado es que un par de usuarios han conseguido registrarse con su correo igual 2 veces. Básicamente lo que estaba pasando es que estábamos guardando Correo@usuario.com y correo@usuario.com como dos correos distintos y son el mismo. Como solución, antes de guardar un usuario en base de datos lo que hacemos es un toLowerCase() y a chuparla. Este fallo ha sido el típico de poco testeo.

  • Unos cuantos usuarios nos estaban comentando que el menú que estaban creando se les mostraba en español en vez de en inglés o que palabras como "Beefeater" se les traducian a "Alabardo de la torre de Londres". Investigando nos damos cuenta de que era por culpa del navegador (Chrome en este caso) que si por casualidad tienes de idiomas puesto el inglés el primero te traduce de forma automática. La solución ha sido meterle un meta en la cabecera para que google no traduzca. No me fio mucho de esta solución pero bueno vamos a ver que pasa.

Aquí acaban los bugs de hoy, ahora seguimos con las features:

  • Se ha añadido a la API un nuevo servicio que permite cambiar la contraseña de un usuario.
  • Se ha añadido a la API un nuevo servicio que permite cambiar el nombre del negocio al usuario.

Para mañana

  • Estoy acabando la parte de backend de "forgot your password" y en cuanto la acabe me pongo con el tema de meterle imágenes a los negocios y pies de página para mostrar info.
Cobre

Ten cuidado con los correos gmail, los puntos(.) es como sino existiesen, osea:
pepito@gmail.com
p.e.p.i.t.o@gmail.com
p.epito@gmail.com
pe.pito@gmail.com,etc...
Serían el mismo email, la gente lo suele usar para registrarse varias veces en una web.
https://support.google.com/mail/answer/7436150?hl=en

2 1 respuesta
kidandcat

Y el + en google igual que el punto, lo mejor para emails es usar un buen regex:

https://emailregex.com/

2 1 respuesta
bornex

#25 No me jodas, no tenía ni idea. Vale lo apunto para que no se nos olvide. Tendré que darle más de una vuelta a esto...
#26 ¿Qué propones? ¿Debería de validar el correo antes de guardar?

1 respuesta
kidandcat

#27 En realidad no vais a conseguir evitar que la gente se registre todas las veces que quiera usando emails, con que controléis lo de las mayúsculas para que no haya confusiones, esta bien. Si queréis ser más estrictos, tendréis que usar algo como el DNI para detectar usuarios únicos.

Y si no, si os motiváis, elegid uno, son fáciles de implementar:

Además estoy seguro de que si les escribís sobre vuestro proyecto y que es open source, os darán un plan de pago sin coste alguno.

1 1 respuesta
bornex

#28 !Vale muchas gracias tio! Por ahora creo que vamos a tirar con la solución perri y ya esta, no quiero gastar más tiempo en cosas como esta que al final no aportan mucho valor al usuario.

bornex

26-Mayo-2020

Hoy no hemos tenido ningún bug reseñable.

Features creadas:

  • Creado todo el backend necesario para hacer el flow de olvidé mi contraseña.

Features empezadas:

  • Poder subir fotos una foto del negocio, lo vamos a hacer con una integración de un servicio web llamado cloudinary para no tener que guardar las fotos en el server (si alguien conoce alguno mejor estaría enormemente agradecido).
  • Perfil de usuario (parte frontend) donde se podrá cambiar la contraseña y cambiar el nombre del negocio.

Usuarios habituales

  • Camp1
  • JuAn4k4
  • bornex
  • s4suk3
  • kidandcat
  • Soltrac
  • HeXaN