¿Pueden una aplicación de rieles y una API de rieles separada compartir una base de datos?

Sí, una combinación de aplicaciones Rails y API puede compartir una base de datos. Esta es una mala idea común que he visto varias veces en los últimos años en clientes mayores / mayores. Si bien es un buen proyecto para un consultor como yo deshacer (largo pero no arriesgado = buena rentabilidad), es mejor no hacerlo en primer lugar.

El enfoque técnico es simple: configura database.yml (o DATABASE_URL en las variables de entorno) para apuntar a la misma base de datos. Voila! Estás compartiendo la base de datos entre dos aplicaciones.

Excepto: la única constante es el cambio, y ahora tienes que revisar dos bases de código sincronizadas. Veamos algunos problemas comunes y sus soluciones que causan problemas:

Migraciones : ¿en qué aplicación viven? “Ambos” es la respuesta más común cuando las dos aplicaciones son mantenidas por diferentes equipos. Alguien que solo se preocupe por una aplicación debe recordar tener una copia de la otra y realizar migraciones o su desarrollo local misteriosamente no estará sincronizado con la producción.

schema.rb : ¿Cuál es canónico? La respuesta es que ambos deben ser, lo que significa que deben estar sincronizados. Cada vez que la aplicación ejecuta una migración que la otra no, no están sincronizados. Esperemos que pueda limitar esto a solo unos segundos durante las implementaciones. Ahora, sé que los expertos en git dicen:

Git submodules / subtrees : puedo garantizar que no confirmará los submódulos y sus dependencias correctamente. Nadie puede, la interfaz de usuario de la línea de comando se entiende mejor como un templo de una película de Indiana Jones: tiene un tesoro brillante, pero cada pasillo de apariencia normal está lleno de trampas mortales. Los subárboles son más confiables, pero hará que los conflictos de fusión formen parte de su vida diaria. Tampoco conozco ninguna de las GUI de git bonitas, por lo que está buscando capacitación adicional en línea de comandos para todo su equipo. Oh, pero nos hemos perdido algo importante:

Modelos : deben estar sincronizados. O no debería. ¿Qué? Tienen que permanecer en perfecta sincronización en las validaciones porque Rails no tiene un buen soporte para las restricciones de la base de datos, por lo que cada vez que las dos tengan validaciones diferentes, una de las aplicaciones se sorprenderá y se desanimará al saber que Model.find(123).valid? puede devolver false . Si las dos aplicaciones tienen propósitos diferentes para los modelos, pueden (¡y deberían!) Tener un código diferente en los modelos, aunque los desarrolladores que trabajan en ambas tendrán dificultades para recordar que Order#finalize solo envía confirmaciones por correo electrónico en la aplicación, pero no en la API porque la API es utilizada por dropshippers que manejan su propio compromiso con el cliente, etc. ActiveRecord viola el Principio de Responsabilidad Única, por lo que realmente lo que necesita hacer es comprender cada propósito de los modelos (desinfección de entrada del usuario, acceso a la base de datos, normalización de datos, lógica de dominio, basura de subtipo) cajón …) y mantenga exactamente el subconjunto correcto de ellos sincronizados.

Gemas : ¡Mueva las migraciones y modelos a una gema compartida! Sí, he visto esta estrategia. Ahora tiene un tercer repositorio git para mantener las sincronizaciones sincronizadas con … con la ventaja de que debe mantener manualmente la especificación de la versión del Gemfile . Y su historia de implementación debe tener en cuenta el acceso de lectura a su repositorio de GitHub. Y ahora un montón de sus dependencias se cargan de forma transitiva desde la gemspec de la gema (por lo que cualquiera que toque la gema necesita saber cómo se usan en las aplicaciones) o se repite en la aplicación Gemfile (se desviarán de la sincronización perfecta y usted tendrá algunos errores increíblemente sutiles y terribles cuando se cargan dos versiones a la vez). Pero todavía hay un último asidero para arañar mientras caemos de los acantilados de la desesperación:

Un repositorio para gobernarlos a todos : ¡calentarse! Coloque ambas aplicaciones en un repositorio de git y tenga un enlace simbólico que necesita del otro: schema.rb , db/migrate schema.rb , app/models , spec/models , quizás partes de lib . No puede hacer cosas diferentes en los modelos a menos que sea realmente poco entusiasta acerca de cómo usa los modelos AR, pero eso puede no ser una gran pérdida. No he tenido que soportar Windows profesionalmente durante unos años, pero creo que su compatibilidad con enlaces simbólicos aún no es excelente, esto puede ser un factor decisivo para usted. He visto funcionar este enfoque.


OK, Smartypants Mc Grizzled Veterano, ¿cuál es el enfoque correcto?

La respuesta a “Me duele cuando hago esto” es “Así que no hagas eso”. Sé de dos alternativas.

Primero, haga que su aplicación Rails sea un consumidor de su API. Use ActiveResource o una de las otras gemas de consumo de API para golpear la API. Puede revolver por separado, modelar datos por separado y no tiene que mantenerlos sincronizados. El costo de la compensación es que tiene una aplicación web más lenta con nuevos modos de falla como API inactiva / lenta, cambios importantes, etc. Pero está comiendo su propio alimento para perros en la API, lo cual es bueno para satisfacer a los clientes externos de API.

El mejor enfoque es desechar la idea de aplicaciones separadas. Haga que su aplicación Rails sirva a su API. Lo sé, los microservicios son geniales y quieres poder escalarlos por separado porque la API es mucho más delgada que la aplicación, pero de todos modos necesitas un plan de escala para tu aplicación, y vale la pena minimizar las partes móviles con un genérico ” servidor de aplicaciones “en el que pones el dial. Todo está estrechamente acoplado y mal delineado: esa es la forma de Rails.

Lamento que esto haya resultado un poco sarcástico, pero es algo que no es extraño y los enfoques tienen modos de falla no obvios porque el tema viola muchos supuestos no declarados de Rails como “Soy la única base de código que habla directamente con esta base de datos “que dificulta innecesariamente el crecimiento de aplicaciones grandes y confiables. Estoy buscando mi mejor código, si quieres unirte a mí en ese viaje.

Para mí, esto depende tanto del punto de vista desde el que se plantea la pregunta como del contexto de “compartir”.

Si el punto de vista es “pueden dos aplicaciones separadas compartir una marca de base de datos (MySQL / PostgreSQL / etc)”, la respuesta es que pueden hacerlo totalmente. No hay ningún problema con esto.

Si el punto de vista es “¿pueden dos aplicaciones separadas usar el mismo servicio de base de datos (ambas configuradas para usar la misma instancia de MySQL / PostgreSQL / etc)”, la respuesta también es que esto es totalmente aceptable (y a menudo ventajoso).

Si el punto de vista es “pueden dos aplicaciones separadas compartir una base de datos dentro de un servicio de base de datos” … la respuesta es “tal vez”. Como otros han señalado, cosas como las migraciones se convierten en un problema cuando esto se hace a menos que las dos aplicaciones usen literalmente las mismas definiciones de tabla (y solo una realmente puede usarse para migrar las tablas). Es * posible *, pero no es una configuración estándar, y personalmente creo que hacerlo sería una decisión de implementación muy pobre (basada no solo en la configuración especializada, sino también en las implicaciones de seguridad de compartir datos entre dos aplicaciones dispares) .

Recientemente me enfrenté a este problema. Principalmente tenemos tres soluciones.

  • Use el recurso activo, cree una nueva clase y en esa clase obtenga datos de la API / XML: busque el recurso activo y cómo trabajar con él. Ahora, cuando tenga usuarios y ámbitos en su API principal, esto tomará algún tiempo (aunque enfrenté algunos problemas con el análisis XML con rails 5, use también json en la URL)
  • Use la API JSON: simplemente cree algunas rutas para el análisis basado en API o use el formato respond_to blocks desde la aplicación principal con la base de datos. Estaba enfrentando problemas de autenticación de usuarios y la complejidad estaba aumentando. Así que hice algunos puntos finales de API que podrían usarse para obtener todos los datos necesarios. Ahora, permita que los datos de la API estén disponibles en su otra aplicación, use el JSON usando HTTParty en una clase y modifique / escriba nuevos métodos de instancia / clase para crear nuevos ámbitos y ajustar el análisis de datos basado en el usuario. Luego, obtenga los resultados en una matriz para iterar en las vistas. Puede trabajar con la solicitud GET y POST por esos métodos también para cada punto final de API y métodos de clase de llamada en la APLICACIÓN 2 cuando sea necesario (Esto es lo que estoy usando ahora): Aquí hay un video blog que muestra esta solución de trabajo:
  • Use Oath2, aunque esto será complejo (todavía no entiendo todos y cada uno de los detalles sobre juramento y juramento2)

Sí, aunque la implementación puede variar.

Una posibilidad sería tener aplicaciones separadas por completo y simplemente hacer que su uso de API sea de solo lectura. Realice el método self.table_name y asegúrese de agregar:

def solo lectura?
cierto
fin

a la clase para que no puedas escribir con la API. En lugar de migraciones, escriba las clases manualmente, para que no estropee la tabla de esquema.


La otra posibilidad sería simplemente que tanto la API como la aplicación usen la misma aplicación de rieles, pero monten la API como un espacio de nombres dentro de la aplicación. Puede tener sus propios controladores, vistas, rutas, etc. Este es probablemente el método preferido a menos que tenga una buena razón para hacerlo de manera diferente (es decir, permisos externos o algo así). Siempre puede usar permisos de nivel Apache para restringir la forma en que las personas acceden a su aplicación frente a cómo acceden a la API, si no desea utilizar otros medios.