La aplicación de base de datos de N capas sin utilizar un ORM, ¿cómo especifica la interfaz de usuario lo que necesita para mostrar los datos?

Estoy buscando indicaciones e información aquí, haré esta CW ya que sospecho que no tiene una sola respuesta correcta. Esto es para C #, por lo que haré algunas referencias a Linq a continuación. También me disculpo por el largo post. Permítame resumir la pregunta aquí, y luego la pregunta completa sigue.

Resumen: en una aplicación de 4 capas UI / BLL / DAL / DB, ¿cómo pueden los cambios en la interfaz de usuario, mostrar más columnas (por ejemplo, en una cuadrícula), evitar fugas a través de la capa de lógica empresarial y en la capa de acceso a datos, para obtener los datos para mostrar (asumiendo que ya se encuentra en la base de datos).

Asumamos una aplicación en capas con 3 (4) capas:

Interfaz de usuario (UI)Capa de lógica de negocios (BLL)Capa de acceso a datos (DAL)Base de datos (DB; la 4ª capa)

En este caso, el DAL es responsable de construir sentencias de SQL y ejecutarlas en la base de datos, devolviendo datos.

¿Es la única forma de "correctamente" construir una capa de este tipo para hacer siempre "seleccionar *"? Para mí eso es un gran no-no, pero déjame explicarte por qué me estoy preguntando.

Digamos que quiero, para mi UI, mostrar a todos los empleados que tienen un registro de empleo activo. Por "activo" quiero decir que los registros de empleo desde-hasta las fechas contienen hoy (o tal vez incluso una fecha que puedo configurar en la interfaz de usuario).

En este caso, digamos que quiero enviar un correo electrónico a todas esas personas, así que tengo un código en el BLL que asegura que aún no he enviado un correo electrónico a las mismas personas, etc.

Para el BLL, necesita cantidades mínimas de datos. Tal vez invoca la capa de acceso a datos para obtener esa lista de empleados activos, y luego una llamada para obtener una lista de los correos electrónicos que ha enviado. Luego se une a esos y construye una nueva lista. Quizás esto podría hacerse con la ayuda de la capa de acceso a datos, esto no es importante.

Lo importante es que para la capa empresarial, realmente no hay mucha información que necesite. Tal vez solo necesite el identificador único para cada empleado, para ambas listas, para coincidir, y luego decir "Estos son los identificadores únicos de aquellos que están activos, a los que aún no ha enviado un correo electrónico". ¿Luego construyo un código DAL que construye sentencias SQL que solo recuperan lo que necesita la capa de negocios? Es decir. solo "SELECCIONAR ID de los empleados DONDE ..."?

¿Qué hago entonces para la interfaz de usuario? Para el usuario, quizás sería mejor incluir mucha más información, dependiendo depor qué Quiero enviar correos electrónicos. Por ejemplo, es posible que desee incluir alguna información de contacto rudimentaria, o el departamento para el que trabajan, o el nombre de sus gerentes, etc., por no decir que, al menos, nombre y dirección de correo electrónico para mostrar.

¿Cómo obtiene la UI esos datos? ¿Cambio el DAL para asegurarme de devolver los datos suficientes a la interfaz de usuario? ¿Cambio el BLL para asegurarme de que devuelve datos suficientes para la interfaz de usuario? Si el objeto o las estructuras de datos devueltos desde el DAL al BLL también pueden enviarse a la interfaz de usuario, tal vez el BLL no necesite un gran cambio, pero los requisitos de la interfaz de usuario afectan una capa más allá de lo que debería comunicarse. . Y si los dos mundos operan en estructuras de datos diferentes, probablemente habrá que hacer cambios en ambos.

¿Y entonces, cuando se cambia la interfaz de usuario, para ayudar al usuario aún más, al agregar más columnas, a qué profundidad debería / debo ir para cambiar la interfaz de usuario? (Suponiendo que los datos ya están presentes en la base de datos, no es necesario realizar ningún cambio allí).

Una sugerencia que surgió es usar Linq-To-SQL e IQueryable, de modo que si el DAL, que se ocupa de qué (según qué tipo de datos) y por qué (como en las cláusulas WHERE) devolvió IQueryables, el BLL podría potencialmente devolverlos a la interfaz de usuario, que luego podría construir una consulta Linq que recuperaría los datos que necesita. El código de la interfaz de usuario podría tirar de las columnas que necesita. Esto funcionaría, ya que con IQuerables, la IU terminaría ejecutando la consulta, y luego podría usar "seleccionar nuevo {X, Y, Z}" para especificar lo que necesita, e incluso unirse en otras tablas, si es necesario.

Esto me parece desordenado. Que la interfaz de usuario ejecuta el código SQL en sí, a pesar de que se ha ocultado detrás de una interfaz de Linq.

Pero, para que esto suceda, no se debe permitir que el BLL o el DAL cierren las conexiones de la base de datos, y en un tipo de mundo IoC, el servicio DAL podría eliminarse un poco antes de lo que le gustaría al código UI, de modo que La consulta Linq podría terminar con la excepción "No se puede acceder a un objeto eliminado".

Así que estoy buscando punteros. ¿Qué tan lejos estamos? ¿Cómo estás tratando con esto? Considero que el hecho de que los cambios en la interfaz de usuario se filtren a través del BLL y en el DAL es una solución muy mala, pero en este momento no parece que podamos hacerlo mejor.

Por favor, dime cuán estúpidos somos y demuéstreme que estoy equivocado.

Y tenga en cuenta que este es un sistema heredado. El cambio del esquema de la base de datos no se encuentra en el alcance durante años, por lo que una solución para usar objetos ORM que esencialmente haga el equivalente a "seleccionar *" no es realmente una opción. Tenemos algunas tablas grandes que nos gustaría evitar que aparezcan en la lista completa de capas.

Respuestas a la pregunta(2)

Su respuesta a la pregunta