Actualmente estamos utilizando Entity Framework como ORM en algunas aplicaciones web, y hasta ahora, nos ha ido bien, ya que todos nuestros datos se almacenan en una sola base de datos. Estamos utilizando el patrón de repositorio, y tenemos servicios (la capa de dominio) que los utilizan, y devuelven las entidades EF directamente a los controladores ASP.NET MVC.
Sin embargo, ha surgido un requisito para utilizar una API de terceros (a través de un servicio web) que nos proporcionará información adicional relacionada con el usuario en nuestra base de datos. En nuestra base de datos de usuarios local, almacenaremos una identificación externa que podemos proporcionar a la API para obtener información adicional. Hay bastante información disponible, pero en aras de la simplicidad, una de ellas se refiere a la empresa del usuario (nombre, gerente, sala, cargo, ubicación, etc.). Esta información se usará en varios lugares a través de nuestras aplicaciones web, en lugar de usarse en un solo lugar.
Entonces mi pregunta es, ¿dónde está el mejor lugar para poblar y acceder a esta información? Como se usa en varios lugares, no es realmente sensato buscarlo ad-hoc donde sea que lo usemos en la aplicación web, por lo que tiene sentido devolver estos datos adicionales de la capa de dominio.
Mi pensamiento inicial fue crear una clase de modelo de contenedor que contendría la entidad EF (EFUser), y una nueva clase 'ApiUser' que contiene la nueva información, y cuando obtenemos un usuario, obtenemos el EFUser y luego obtenemos el adicional información de la API y rellene el objeto ApiUser. Sin embargo, si bien esto estaría bien para obtener usuarios individuales, se cae cuando se obtienen múltiples usuarios. No podemos alcanzar la API cuando obtenemos una lista de usuarios.
Mi segundo pensamiento fue simplemente agregar un método singleton a la entidad EFUser que devuelve el ApiUser, y completarlo cuando sea necesario. Esto resuelve el problema anterior ya que solo accedemos a él cuando lo necesitamos.
O el pensamiento final fue mantener una copia local de los datos en nuestra base de datos y sincronizarla con la API cuando el usuario inicia sesión. Esto es un trabajo mínimo, ya que es solo un proceso de sincronización, y no tenemos la carga de golpear. la base de datos y la API cada vez que queremos obtener información del usuario. Sin embargo, esto significa almacenar los datos en dos lugares, y también significa que los datos están desactualizados para cualquier usuario que no haya iniciado sesión por un tiempo.
¿Alguien tiene algún consejo o sugerencia sobre la mejor manera de manejar este tipo de escenario?
fuente
it's not really sensible to fetch it on an ad-hoc basis
-- ¿Por qué? Por razones de rendimiento?Respuestas:
Tu caso
En su caso, las tres opciones son viables. Creo que la mejor opción es probablemente sincronizar sus fuentes de datos en algún lugar que la aplicación asp.net ni siquiera conoce. Es decir, evite las dos recuperaciones en primer plano cada vez, sincronice la API con la base de datos en silencio). Entonces, si esa es una opción viable en su caso, le digo que lo haga.
Una solución en la que realiza la búsqueda 'una vez' como sugiere la otra respuesta no parece muy viable, ya que no persiste la respuesta en ningún lado y ASP.NET MVC solo hará la búsqueda para cada solicitud una y otra vez.
Evitaría el singleton, no creo que sea una buena idea por muchas de las razones habituales.
Si la tercera opción no es viable, una opción es cargarla perezosamente. Es decir, haga que una clase extienda la entidad y haga que llegue a la API según sea necesario . Sin embargo, esa es una abstracción muy peligrosa, ya que es aún más mágico y un estado no obvio.
Supongo que realmente se reduce a varias preguntas:
Una o dos palabras sobre la separación de preocupaciones
Permítanme argumentar en contra de lo que dice Bobson sobre la separación de preocupaciones aquí. Al final del día, poner esa lógica en entidades como esa viola la separación de las preocupaciones igual de malo.
Tener un repositorio de este tipo viola la separación de las preocupaciones de la misma manera al poner la lógica centrada en la presentación en la capa de lógica de negocios. Su repositorio ahora se da cuenta de repente de las cosas relacionadas con la presentación, como cómo muestra al usuario en sus controladores aspvc mvc.
En esta pregunta relacionada , he preguntado sobre el acceso a entidades directamente desde un controlador. Permítanme citar una de las respuestas allí:
(Lea el resto de la respuesta, es realmente bueno).
Es ingenuo ignorar el hecho de que hay una base de datos , hay una base de datos, y no importa cuán duro quiera abstraer eso, no va a ninguna parte. Su aplicación estará al tanto de la fuente de datos. No podrá 'intercambiarlo en caliente'. Los ORM son útiles, pero se filtran debido a lo complicado que es el problema que resuelven y por muchas razones de rendimiento (como Seleccionar n + 1, por ejemplo).
fuente
Con una separación adecuada de las preocupaciones , nada por encima del nivel de Entity Framework / API debería darse cuenta de dónde provienen los datos. A menos que la llamada a la API sea costosa (en términos de tiempo o procesamiento), el acceso a los datos que lo utiliza debe ser tan transparente como el acceso a los datos desde la base de datos.
La mejor manera de implementar esto, entonces, sería agregar propiedades adicionales al
EFUser
objeto que cargue de manera diferida los datos de la API según sea necesario. Algo como esto:Externamente, la primera vez que se use
CompanyName
oJobTitle
habrá una sola llamada API (y, por lo tanto, un pequeño retraso), pero todas las llamadas posteriores hasta que se destruya el objeto serán tan rápidas y fáciles como el acceso a la base de datos.fuente
Una idea es modificar ApiUser para que no siempre tenga la información adicional. En cambio, pones un método en ApiUser para buscarlo:
También puede modificar esto ligeramente para usar la carga diferida de datos adicionales, de modo que no tenga que extraer UserExtraData del objeto ApiUser:
De esta manera, cuando tenga una lista, los datos adicionales no se recuperarán de forma predeterminada. ¡Pero aún puede acceder a él mientras recorre la lista!
fuente