10

Estoy implementando un DAL usando el marco de entidad. En nuestra aplicación, tenemos tres capas (DAL, capa de negocios y presentación). Esta es una aplicación web. Cuando comenzamos a implementar el DAL, nuestro equipo pensó que DAL debería tener clases cuyos métodos reciban un ObjectContext dado por los servicios en la capa empresarial y operar sobre él. El fundamento de esta decisión es que diferentes ObjectContexts ven diferentes estados de DB, por lo que algunas operaciones pueden rechazarse debido a problemas con la coincidencia de claves foráneas y otras incoherencias.¿Es el patrón DTO plus UnitOfWork un buen enfoque para diseñar un DAL para una aplicación web?

Notamos que generar y propagar un contexto de objetos desde la capa de servicios genera un alto acoplamiento entre las capas. Por lo tanto, decidimos usar DTO mapeados por Automapper (entidades no administradas o entidades de auto-seguimiento que argumentan alto acoplamiento, exponiendo entidades a capas superiores y baja eficiencia) y UnitOfWork. Entonces, aquí están mis preguntas:

  1. ¿Es este el enfoque correcto para diseñar el DAL de una aplicación web? ¿Por qué?
  2. Si respondió "sí" a 1., ¿cómo se concilia el concepto de DTO con los patrones de UnitOfWork?
  3. Si respondió "no" a 1., ¿cuál podría ser un enfoque correcto para diseñar un DAL para una aplicación web?

Por favor, si es posible, brinde bibliografía que respalde su respuesta.

Sobre el diseño actual:

La aplicación ha sido planeado para ser desarrollado en tres capas: presentación, negocio y DAL. La capa empresarial tiene fachadas y servicios

Existe una interfaz llamada ITransaction (con solo dos métodos para deshacerse y guardar cambios) solo visible en los servicios. Para administrar una transacción, hay una Transacción de clase que extiende un ObjectContext e ITransaction. Hemos diseñado esto teniendo en cuenta que en la capa empresarial no queremos que otros métodos ObjectContext sean accesibles.

En el DAL, creamos un repositorio abstracto usando dos tipos genéricos (uno para la entidad y el otro para su DTO asociada). Este repositorio tiene métodos CRUD implementados de forma genérica y dos métodos genéricos para asignar los DTO y las entidades del repositorio genérico con AutoMapper. El constructor de repositorio abstracto toma un ITransaction como argumento y espera que ITransaction sea un ObjectContext para asignarlo a su propiedad ObjectContext protegida.

Los repositorios de hormigón solo deben recibir y devolver tipos .net y DTO.

Ahora nos enfrentamos a este problema: el método genérico para crear no genera una identificación temporal o persistente para las entidades adjuntas (hasta que usemos SaveChanges(), rompiendo la transaccionalidad que queremos); esto implica que los métodos de servicio no pueden usarlo para asociar los DTO en el BL)

Respuesta

7

Hay una serie de cosas que están sucediendo aquí ... La suposición que haré es que está usando una arquitectura de 3 niveles. Dicho esto, no estoy seguro de algunas decisiones de diseño que ha tomado y de las motivaciones que las motivaron. En general, diría que su ObjectContext no se debe pasar en sus clases. Debería haber algún tipo de clase de gestor o repositorio que maneje la gestión de conexión. Esto resuelve su problema de administración del estado de DB. Me parece que un patrón de repositorio funciona muy bien aquí. A partir de ahí, podrá implementar el patrón de unidad de trabajo con bastante facilidad ya que su gestión de conexión se manejará en un solo lugar.Dado lo que sé sobre su arquitectura, diría que debería usar una estrategia POCO. El uso de POCO no lo relaciona estrechamente con ningún proveedor de ORM. La ventaja es que sus POCO podrán interactuar con su ObjectContext (probablemente a través de Repository de algún tipo) y esto le dará visibilidad sobre el seguimiento de cambios. Una vez más, desde allí podrá implementar el patrón de unidad de trabajo (transacción) para darle control total sobre cómo debe comportarse su transacción comercial. Encuentro que este es un artículo increíblemente útil para explicar cómo encaja todo esto. El código tiene errores, pero ilustra con precisión las mejores prácticas para el tipo de arquitectura que está describiendo: Repository, Specification and Unit of Work Implementation

La versión corta de mi respuesta a la pregunta número 1 es "no". El enlace anterior proporciona lo que creo que es un mejor enfoque para usted.

+0

Mire la última versión de la pregunta ... ¡gracias! – JPCF

+0

enlace pendiente ...! – JPCF

2

Debe ver lo que significa dependency injection y la inversión de control en general. Eso proporcionaría la capacidad de controlar el ciclo de vida de ObjectContext "desde afuera". Puede asegurarse de que solo se utiliza 1 instancia de contexto de objeto para cada solicitud http. Para evitar administrar dependencias manualmente, recomendaría usar StructureMap como contenedor.

Otra técnica útil (pero bastante complicada y difícil de hacer bien) es la abstracción de la persistencia. En lugar de usar ObjectContext directamente, utilizaría el llamado Repository que es responsable de proporcionar una colección como API para su almacén de datos. Esto proporciona una útil costura que puede usar para cambiar el mecanismo de almacenamiento de datos subyacente o para simular completamente la persistencia para las pruebas.

Como ya sugirió Jason - También debería usar POCO`s (objetos antiguos de clr). A pesar de que todavía existiría un acoplamiento implícito con el marco de la entidad. Usted debe tener en cuenta que es mucho mejor que usar clases generadas.

cosas que no encontrará en otro lugar lo suficientemente rápido:

  1. tratar de evitar el uso de unit of work. Su modelo debe definir límites transaccionales.
  2. Trate de evitar el uso de generic repositories (observe también IQueryable).
  3. No es obligatorio enviar correo no deseado con repository pattern name.

Además, puede disfrutar leyendo sobre domain driven design. Ayuda a manejar la lógica empresarial compleja y brinda excelentes pautas para que el código sea menos procesal y más orientado a objetos.

+0

mire la última versión de la pregunta ... ¡gracias! – JPCF

+0

¿Tiene un ejemplo de DDD con EF? Me interesa especialmente cómo define los límites transaccionales. –

+0

@Ladislav desafortunadamente, no estoy muy familiarizado con EF. tipo creer ciegamente que NHibernate es aún superior. pero eso cambiaría solo la parte del repositorio. los límites transaccionales son dibujados por las raíces agregadas en sí mismas porque son responsables del estado válido y persisten en la operación atómica. el modelado correcto de ellos es la clave. –

1

Me centraré en sus problemas actuales: para ser sincero, no creo que deba pasar su ObjectContext. Creo que eso generará problemas. Supongo que un controlador o un servicio comercial pasará ObjectContext/ITransaction al Repositorio. ¿Cómo se asegurará de que su ObjectContext se elimine correctamente en el proceso? ¿Qué sucede cuando usa transacciones anidadas? ¿Qué gestiona los retrocesos, para las transacciones posteriores?

Creo que su mejor apuesta es dar una definición más aproximada de cómo espera gestionar las transacciones en su arquitectura. Usar TransactionScope en su controlador/servicio es un buen comienzo ya que ObjectContext lo respeta. Por supuesto, es posible que deba tener en cuenta que los controladores/servicios pueden realizar llamadas a otros controladores/servicios que tienen transacciones en ellos. Para permitir escenarios en los que desee un control total sobre sus transacciones comerciales y las posteriores llamadas a la base de datos, deberá crear algún tipo de clase de TransactionManager que se enlista y, en general, gestione las transacciones hacia arriba y hacia abajo de su pila.Descubrí que NCommon hace un trabajo extraordinario tanto en el resumen como en la gestión de transacciones. Eche un vistazo a las clases de UnitOfWorkScope y TransactionManager allí. Aunque estoy en desacuerdo con el enfoque de NCommon de obligar al repositorio a confiar en UnitOfWork, eso podría refactorizarse fácilmente si así lo desea.

En cuanto a su problema persistantID va, check this out

+0

Antes de todo, ¡gracias! pareces estar muy interesado Creamos una transacción en los servicios pero utilizamos ITransaction; esta interfaz expone solo dos métodos: Dispose y Save Chances. Un objeto Transaction es un ObjectContext pero se trata como eso en DAL; los servicios solo ven ITransaction. Guardar y desechar una transacción es responsabilidad del programador en los métodos de servicio. No implementamos transacciones anidadas y son simples, por lo que no consideramos reversiones. – JPCF

4

Siempre he creído que código puede explicar mejor las cosas de mundos para los programadores. Y esto es especialmente cierto para este tema. Es por eso que le sugiero que mire la gran aplicación de muestra en la que se implementan todas las consecuencias que espera.

alt text

proyecto se llama Sharp Architecture, que se centra alrededor de MVC y NHibernate, pero se puede utilizar el mismo enfoque sólo la sustitución de piezas con NHibernateEF queridos cuando los necesite. El objetivo de este proyecto es proporcionar una plantilla de aplicación con todas las mejores prácticas de la comunidad para crear aplicaciones web.

Cubre todas comunes y la mayoría de los temas no comunes al usar ORM, la gestión de las transacciones, la gestión de dependencias con contenedores IoC, uso de dtos, etc.

Y aquí es un sample application.

Insisto en leer y probar esto, será una verdadera aventura para ti como lo fue para mí.

Cuestiones relacionadas