2011-03-30 9 views
5

Estoy agregando funcionalidad a nuestro sitio web que realiza procesos de larga ejecución de forma asincrónica utilizando MSMQ. Sin embargo, hacer este ansynch significa que debemos notificar a los usuarios cuando se completen sus solicitudes. Utilizando el patrón de comando, creé una interfaz * llamada INotify y la compuse en la clase de mensaje, por lo que la clase de procesamiento de mensaje simplemente puede llamar a GiveNotice() en el objeto INotify del mensaje. La primera implementación, EmailNotify, fue más difícil de lo esperado, ya que me sorprendió descubrir que MailMessage no es serializable, pero lo puso en marcha.implementando objetos de funcionalidad de base de datos en una arquitectura N-Tier?

Ahora estoy trabajando en un nuevo notificador concreto, DBNotify, que llamará a un SP de algún tipo y actualizará un estado en la base de datos transaccional principal. He tropezado con que me gustaría volver a utilizar la arquitectura DAL que ya hemos creado, pero INotify es miembro del proyecto Model, que es más fundamental que el DAL.

Nuestra jerarquía se ve así: Común> Modelo> DAL> BAL

Aquí hay más detalles acerca de los niveles. Tenga en cuenta que heredé esto de: Common es responsable de todas las funciones de "utilidad" que se usan en muchos lugares de la aplicación, como el acceso a la configuración, el análisis de cadenas de caracteres y la funcionalidad no relacionada con el negocio.

El modelo son objetos comerciales, lo que algunos llaman objetos de transferencia de datos, colecciones de getters y setters. He agregado algunas "herramientas inteligentes" en esta capa, pero solo reglas de negocio internas para ese objeto, como "El nombre de un elemento debe comenzar con un carácter alfanumérico".

DAL es la capa de acceso a los datos, en teoría, todo lo que sucede aquí es que los objetos modelo se mueven dentro y fuera de la base de datos.

BAL es la capa Business; en teoría, las reglas comerciales que rigen la interacción de los objetos se aplican (es decir, "un formulario debe tener al menos dos elementos").

Por lo tanto, la interfaz de INotify se define como una abstracción para permitir que el método de notificación varíe de forma independiente (es decir, correo electrónico, TXT, twitter, etc.). Es fundamental para el sistema, por lo que lo he creado en el nivel Modelo, que es independiente del nivel DAL. Sin embargo, estoy creando una nueva implementación concreta de INotify cuyo método de notificación es llamar a un SP en una base de datos.

¿Alguien más ha tratado con un objeto comercial cuyo objetivo es interactuar con una base de datos, y cómo lo sitúa en su arquitectura N-tier?

Antes de decirme que use Linq en Sql, muchas gracias. Esta no es una pregunta técnica (¿cómo hago esto?), Es una pregunta de diseño (¿cómo debo hacer esto?).

Creo que hay un sitio StackExchange más centrado en este tipo de preguntas de diseño independientes del lenguaje, así que voy a copiar esto allí.

+0

* Interfaz sólo de nombre, ya que en realidad quiero serializar estos objetos, que tenía que hacer una clase abstracta. –

+1

Estoy confundido acerca de su jerarquía. ¿De qué son responsables el Modelo y el BAL? Tomé BAL para representar Business Access Layer pero luego el Modelo parece estar fuera de lugar. En el código que he visto, el modelo suele ser el más abstracto y se encuentra arriba (usos) o es parte de la capa empresarial ... Además, si el modelo es más fundamental que el DAL, ¿cuál es el problema con las clases que usan DAL? desde y llamando a métodos en el Modelo? –

+0

Ah, y en caso de que no lo supiera, no necesita agregar información en comentarios, simplemente puede editar su pregunta ... –

Respuesta

0

Gracias a todos por sus comentarios, hay varias ideas aquí para mejoras pienso implementar, aunque ninguno responde directamente a la pregunta que me estaba preguntando.

Publiqué esto a los Programadores, donde creo que este tipo de pregunta puede realmente pertenecer, y obtuve algunas ideas útiles. Si está interesado, el hilo está aquí: Programmers thread on this issue. Es cierto que agregué la "pista" de la inyección de dependencia en base a mi propia investigación cuando publiqué allí, por lo que el problema puede haber sido más claro.

Esta es una gran y útil de la comunidad, que estoy muy orgulloso de participar en.

1

Si sus clases de modelo son sus DTO (lo que algunos podrían llamar estructuras de datos o tipos de datos), deberían (probablemente) colocarse "a través" de sus otras capas y conocerlas a todas.

Según lo que diga, puede tener una clase MessageProcessing que se encuentra en el BAL y recibe mensajes de otras partes del BAL o DAL, luego notifica a cualquiera que esté escuchando (la UI u otros miembros interesados ​​del BAL)

+0

Los mensajes entran en MSMQ y se procesan mediante un código que se ejecuta como un servicio, por lo que la creación de una clase MessageProcessing dentro de la aplicación web no ayuda.Pero tu respuesta me dio una idea. –

2

El hecho de que su interfaz INotify esté en su capa de Modelo no significa que todas las implementaciones concretas deban estar allí.DEBERÍA ser una interfaz: el objetivo de una interfaz es lograr la abstracción, no una clase base, el objetivo de una clase base es lograr la funcionalidad compartida. Entonces, en cualquier lugar donde tenga una propiedad o parámetro de este tipo en cualquiera de sus capas, debe declararse como INotify. En su BAL (¿se refería a BLL, Business Logic Layer?), Decidiría el tipo concreto que se utilizará para las instancias de INotify. Dependiendo de qué tan complejas sean sus notificaciones, podría definir su implementación concreta en su BLL y hacer que use una clase auxiliar en el DAL para realizar la llamada a su sproc, o podría definirla como una clase directamente en su DAL ya que interactúa con la base de datos; eso es realmente una decisión basada en cuánto es responsable la clase; De cualquier manera, debe ser accesible en la capa superior.

¿Alguien más ha tratado con un objeto de negocio cuyo propósito es interactuar con una base de datos y cómo lo ubica en su arquitectura N-tier?

La forma en que sus proyectos están estructurados, parece que las responsabilidades lógicas de cada capa sería:

común: métodos de utilidad compartidos con ninguna dependencia de cualquier otra cosa en el proyecto
Modelo : Define la estructura de las entidades en el sistema, también denominadas DTO u Objetos de transferencia de datos, lo que significa que pueden transferirse entre capas. Todo lo que hacen es almacenar sus datos y realizar una validación básica.
DAL: Responsable de crear instancias de clases desde su capa de Modelo y establecer las propiedades basadas en los valores almacenados en un repositorio, como una base de datos. También es responsable de hacer un seguimiento de los cambios a las entidades del Modelo, y guardar (persistir) esos cambios en el repositorio.
BAL/BLL: Utiliza las clases definidas en las otras capas para lograr algo útil y valida que se cumplan los requisitos del negocio.

Puede lograrlo con una variedad de tecnologías e incluso utilizando la misma tecnología, su implementación exacta variará según cómo trabaje. Algo como Linq2Sql o Entity Framework fuera de la caja desdibujaría las líneas entre su Modelo y su DAL; Quieren definir ambos en el mismo proyecto. Sin embargo, Entity Framework es más flexible y con cierto trabajo puede dividir la definición del modelo de entidad y el "contexto" (para usar el término de Entity Framework), que es el responsable final del componente DAL, en proyectos separados. Puede editar las plantillas T4 o buscarlas en línea que generarán las definiciones de entidad y clase de contexto del modelo de entidad para admitir los patrones de diseño Repositorio y Unidad de trabajo, donde nunca hace referencia directamente a un contexto de entidad sino que implementa una interfaz IRepository , lo que hace que tu código sea más comprobable. Nunca he trabajado personalmente con NHibernate, pero tengo entendido que es capaz de hacer lo mismo (y podría decirse que actualmente puede hacer un mejor trabajo).

+0

buen esfuerzo, aquí hay una sugerencia: las respuestas en SO tienden a ser cortas y al grano. O intente acortarlo o dividirlo en más párrafos y agregar títulos. – jgauffin

+0

INotify es funcionalmente una interfaz, pero no se puede serializar una interfaz, por lo que la convertí en una clase abstracta. Limita la flexibilidad (si fuera una interfaz, podría heredar de otras clases y aún implementar INotify). –

+0

Sí, BAL == BLL. Solo usando el nombre que heredé. –

3

Quizás no sea realmente una respuesta a su pregunta, pero algo en lo que pensar no obstante.

Estoy en desacuerdo con el lugar donde ingresa el acceso a los datos en la jerarquía de sus componentes. No lo pondría entre dos capas de dominio funcional. Ni siquiera "por encima" de las clases de modelo de dominio único. El acceso a datos, o la persistencia, no es una preocupación para ninguna clase de dominio. Solo debería ser algo que se les pueda hacer a ellos, no algo que ellos hagan.

A pesar de que empecé a cabo la codificación cosas como TClient.Save y TClient.Load ahora que he llegado a la conclusión de que no es el cliente el que decide que necesita ser salvado, pero las interacciones del usuario que dictan cuando se necesitan datos de una instancia de dominio y por lo tanto debería cargarse, y cuando los datos de un Cliente deberían persistir, si es que los hay. Por lo tanto, ahora soy un defensor de la codificación (en la GUI, más específicamente en los controladores en la GUI) cosas como DataStore.Load(ClientInstance) y DataStore.Save(ClientInstance). Luego, depende de la capa de acceso a datos averiguar cómo hacerlo. Podría usar la reflexión en C#, o el nuevo RTTI en Delphi para iterar sobre todas las propiedades del Cliente para que pueda enviarlas a una base de datos.

Si bien las capas son un muy buen concepto para separar las preocupaciones y evitar que pongas cosas por todos lados simplemente adhiriéndose a "puedes llamar pero no subir", no ayuda tanto cuando se trata de cosas como el registro. manejo de excepciones, notificaciones y todas esas otras preocupaciones transversales interesantes que cualquier otro componente/capa necesita.

Además, la capa común, ya que es una capa de utilidad, realmente debería ser accesible para todas las demás capas.

para ponerlo todo en un cuadro (donde he guardado la distinción que hace entre las clases simples de dominio, su modelo y reglas de negocio de clase cruzadas, su BAL):

+---+ +-------------+ 
| C |<--| Data Access |<--------------------------+ 
| o | +-------------+       | 
| m |   |         | 
| m |   |         | 
| o |   v         | 
| n | +-------------+ +----------------+ +-----+ 
| |<--| Model  +<--| Cross class |<--| GUI | 
| | +-------------+ | business rules | |  | 
| |      |    | |  | 
| |<--------------------|    | |  | 
| |      +----------------+ |  | 
| |           |  | 
| |<-----------------------------------------|  | 
+---+           +-----+ 

La implementación Inotify que llama en la base de datos se encuentra actualmente en el modelo, que en la imagen anterior, no llama a la capa de acceso a datos en sí, solo se llama, o más bien se interroga, por la capa de acceso a datos.

La pregunta realmente es si INotify debe estar en el "modelo", una parte de la capa de dominio, o si debe ser una interfaz común y debe haber una capa/componente de "Notificación" separada que sea accesible desde el dominio y la GUI. Este nuevo componente podría no solo preocuparse por las notificaciones, sino por muchas otras cuestiones transversales, como el registro. Tiene acceso a los componentes comunes (por supuesto) y de acceso a datos, y a la GUI, al menos en algún tipo de devolución de llamada.

En la siguiente imagen he tratado de visualizar esto, pero no soy muy bueno en la visualización y siempre tengo problemas con esos molestos cortadores transversales. Es por eso que no hay flechas de llamada desde la capa de dominio a las preocupaciones de corte transversal, aunque, por supuesto, la capa de dominio debería poder acceder, por ejemplo, a una interfaz de "Registrador". Tal vez estoy tratando de distinguir entre los componentes comunes y transversales, y podría hacerse un argumento para juntarlos y visualizarlos como bloques separados dentro de una capa/componente de "Utilidad".

 +--------------------------------------------+ 
    +-----| Cross cutting concerns      | 
    |  +--------------------------------------------+ 
    v   v^         ^
+---+ +-------------+        | 
| C |<--| Data Access |<--------------------------+ | 
| o | +-------------+       | | 
| m |   |         | | 
| m |   |         | | 
| o |   v         | v 
| n | +-------------+ +----------------+ +-----+ 
| |<--| Model  +<--| Cross class |<--| GUI | 
| | +-------------+ | business rules | |  | 
| |      |    | |  | 
| |<--------------------|    | |  | 
| |      +----------------+ |  | 
| |           |  | 
| |<-----------------------------------------|  | 
+---+           +-----+ 
1

Puede usar sus entidades de datos en su proyecto si son POCO. De lo contrario, crearía modelos separados como lo has hecho. Pero manténgalas en un ensamblaje aparte (no en el proyecto de DataAccess)

capas de uso excesivo de gente imho. La mayoría de las aplicaciones no necesitan muchas capas. Mi cliente actual tenía una arquitectura como la tuya para todas sus aplicaciones. El problema era que solo la capa de acceso a los datos y la capa de presentación tenían lógica en ellos, todas las otras capas simplemente tomaron datos de la capa inferior, la transformaron y la enviaron a la capa superior.

Lo primero que hice fue decirles que desechar todas las capas y en su lugar usar algo como esto (requiere un contenedor IoC):

  • Core (Contiene reglas de negocio y DATAACCESS través de un ORM)
  • especificación (patrón de interfaz de Seperated. contiene interfaces de servicios y modelos)
  • interfaz de usuario (que podría ser un servicio web, WinForms, webapp)

que funciona para la mayoría de apl icación. Si encuentra que Core crece y se vuelve demasiado grande también puede dividirlo sin afectar a ninguna de las interfaces de usuario.

¿Ya está utilizando un ORM y ha pensado en usar un bloque de validación (FluentValidation o DataAnnotations) para la validación? Facilita la validación de sus modelos en todas las capas.

0

Las clases que se utilizan en muchas capas me preocupan.

Especialmente cuando también están vinculados al modelo de datos/base/capa.

Tan pronto como haya un cambio en estas clases, podría encontrarse con una nueva codificación en todas las capas. En otras palabras, te falta el efecto útil de la abstracción.

Dicho esto, mantener el código de transformación (de capa a capa) tampoco es muy divertido pero, en general, menos trabajo.

Una solución intermedia podría ser el uso de interfaces/roles: defina para cada capa la interfaz/rol que debe jugar un objeto y use esa interfaz para pasarla a la capa. Una clase (compartida) debe implementar un rol (o muchos de ellos). Esto proporcionará un sistema más débilmente acoplado.

he aprendido mucho de this neat lecture about DCI (Data, Collaborations, and Interactions)

Cuestiones relacionadas