2009-06-01 10 views
9

Espero que veas el problema que estoy describiendo en el siguiente escenario. Si no está claro, házmelo saber.¿Dónde se realiza la validación?

Tienes una aplicación que se ha roto en tres capas,

  • frontal capa de interfaz de usuario final, podría ser formulario web asp.net, o ventana (utilizado para los datos persona edición)
  • nivel medio de servicios de negocios capa, compilado en una DLL (PersonServices) capa de acceso
  • datos, recopilados en un archivo DLL (PersonRepository)

En mi extremo delantero, que quieren crear un nuevo objeto persona, establecer algún properti es, como FirstName, LastName según lo que un usuario haya ingresado en la UI, y llame a PersonServices.AddPerson, pasando a la persona recién creada. (AddPerson no tiene que ser estático, esto es solo por simplicidad, en cualquier caso el AddPerson eventualmente llamará a AddPerson del Repositorio, que luego persistirá en los datos.)

Ahora la parte que me gustaría escuchar su la opinión es validación En algún momento, esa persona recién creada necesita ser validada. Puede hacerlo desde el lado del cliente, lo que sería simple, pero ¿qué ocurre si quiero validar la persona en mi método PersonServices.AddPerson? Esto garantizaría que cualquier persona que quiera guardar sea validada y elimina cualquier dependencia en la capa de interfaz de usuario que hace el trabajo. O tal vez, valide tanto en la interfaz de usuario como en la capa de servidor empresarial. ¿Suena bien hasta ahora, verdad?

Así, por simplicidad, voy a actualizar el método PersonService.AddPerson para realizar las siguientes comprobaciones de validación - Compruebe si nombre y apellido no están vacíos - Asegurar esta nueva persona no existe ya en mi repositorio

Y este método devolverá True si toda la validación se aprueba y persiste la persona, False si la Validación falla o si la Persona no se conserva.

Pero este valor booleano que devuelve AddPerson no es suficiente para mí en la capa UI para darle al usuario un motivo claro por el que falló el proceso de guardar. Entonces, ¿qué hace un desarrollador solitario? En última instancia, me gustaría que el método AddPerson sea capaz de garantizar que lo que está a punto de guardar sea válido, y si no, ser capaz de comunicar las razones por las que no es inválido para mi capa de UI.

Solo para que fluya tu jugo, algunas formas de resolverlo podrían ser: (Algunas de estas soluciones, en mi opinión, apestan, pero solo las pongo allí para que entiendas lo que estoy intentando para resolver)

  • En lugar de addPerson devolver un valor lógico, puede devolver un int (es decir, 0 = éxito, distinto de cero es igual a fracaso y el número indica la razón por la que falló.

  • En addPerson, lanzar excepciones personalizadas cuando la validación falla. Cada tipo de excepción personalizada tendría su propio mensaje de error. Además, cada excepción personalizada w Ould ser lo suficientemente única para coger en la capa de interfaz de usuario

  • Tienes addPerson regresar algún tipo de clase personalizada que tendría propiedades que indican si la validación superado o no, y si se omitió, ¿cuáles fueron las razones

  • No

    Asegúrese de que esto se pueda hacer en VB o C#, pero adjunte algún tipo de propiedad a la Persona y sus propiedades subyacentes.Esta propiedad "adjunto" podría contener cosas como información de validación

  • Inserte su idea o patrón aquí

  • Y tal vez otro aquí

Disculpas por la cuestión de largo aliento, pero Definitivamente me gusta escuchar tu opinión sobre esto.

Gracias!

Respuesta

7

Varias capas de validación van bien con aplicaciones multicapa.

La IU en sí puede hacer las verificaciones más simples y rápidas (todos son campos obligatorios presentes, están usando los juegos de caracteres apropiados, etc.) para dar un feedback inmediato cuando el usuario comete un error tipográfico.

Sin embargo, la lógica comercial debe tener la mayor parte de las responsabilidades de validación ... y por una vez no es un problema si esto es "repetitivo", es decir, si la capa empresarial vuelve a verificar algo que ya debería haber sido verificado la interfaz de usuario: el BL debe verificar todas las reglas comerciales (esta doble verificación de la corrección de UI permite múltiples clientes UI diferentes que pueden no ser perfectos en sus verificaciones; por ejemplo, un cliente especial en un teléfono inteligente que puede no tener un buen javascript , y así sucesivamente - y, un poco, protege contra clientes maliciosamente pirateados).

Cuando la lógica de negocio guarda los datos validados "" a la base de datos, que capa debe realizar sus propios controles - DB son buenos en eso, y, de nuevo, no se preocupe por algo de repetición - es la El trabajo de DB para forzar la integridad de los datos (es posible que desee diferentes formas de alimentar datos un día, por ejemplo, un "cargador masivo" para importar un número de personas de otra fuente, y es clave asegurarse de que todas esas formas de cargar datos siempre respeten reglas de integridad de datos); algunas reglas, como la singularidad y la integridad referencial, se aplican mejor en la base de datos, en particular, también por cuestiones de rendimiento.

Cuando el DB devuelve un mensaje de error (datos no insertados como restricción X se violaría) a la capa empresarial, el trabajo de este último es reinterpretar ese error en términos comerciales y enviar los resultados a la interfaz de usuario para informar al usuario; y, por supuesto, el BL también debe proporcionar información clara y completa sobre la infracción de las reglas comerciales a la interfaz de usuario, nuevamente para que se muestre al usuario.

Un "objeto personalizado" es, por tanto, claramente "el único camino a seguir" (en algunos casos, simplemente lo convertiría en un objeto JSON, por ejemplo). Mantener el objeto Person (para mantener su propiedad de "problemas de validación") cuando el DB se rehusó a persistir no parece una técnica clara y simple, así que no creo mucho en esa opción; pero si lo necesita (p.para habilitar la funcionalidad "dime otra vez lo que estaba mal", tal vez si el cliente se fue antes de que la respuesta estuviera lista y necesite reiniciarse sin problemas más tarde; o bien, una lista de tales objetos para auditorías posteriores, & c), entonces el "objeto de validación-falla personalizado" podría también anexarse ​​a esa lista ... pero eso es un "problema secundario", lo principal es para el BL para responder a la IU con un objeto de este tipo (que también podría usarse para proporcionar información útil sin errores si la inserción realmente tuvo éxito).

+0

¿Qué hay de la capacidad de prueba? Considere un ejemplo artificial, que es una versión simplificada de lo que había visto en una aplicación no relacionada. "Shopping.com" crea cupones/promociones en su sitio web con su aplicación web interna "Genie" . El promo genie más corto puede crear será válido durante 6 horas después de su publicación. Para crear (no publicar) una promoción, genie realiza una llamada api que envía el expiryDateTime, , entre otras cosas, al back-end. Tenga en cuenta que la regla de negocios de 6 horas no se puede cambiar por algunas buenas razones ..... (refiérase al siguiente comentario) – testerjoe2

+0

En genie, queremos PRUEBA si se muestra un cuadro de mensaje debajo de la promoción cuando caduque. Pensé en el impacto en las pruebas cuando utilizamos los dos enfoques de diseño a continuación para crear promociones: *** ONE *** - La IU comprueba si el tiempo de expiración es de al menos 6 horas o más desde la hora actual y menos de un mes . Luego pasa, expiryDateTime para back-end. - Backend solo comprueba si expiryDateTime está entre la hora actual y un mes. *** DOS *** - Controles de back-end si el expiryDateTime es entre 6 horas a partir de ahora y un mes ..... (refiérase al comentario siguiente) – testerjoe2

+0

Prueba en los dos enfoques - *** ONE ** * - Use la llamada api para ejecutar el servidor para establecer expiryDateTime a unos minutos de la hora actual. *** DOS *** - Envíe una llamada de API al backend y espere 6 horas para probar el vencimiento. O bien, golpee la capa de la base de datos directamente y cambie expiryDateTime allí, si está permitido. – testerjoe2

1

Mucho de esto es más estilo que sustancia. Personalmente, estoy a favor de devolver objetos de estado como una solución flexible y extensible. Diría que creo que hay un par de clases de validación en juego, la primera es "¿esta información personal se ajusta al contrato de lo que es una persona?" y el segundo es "¿esta persona datos viola las restricciones en la base de datos?" Creo que la primera validación puede y debe hacerse en el cliente. El segundo debe hacerse en el nivel medio. Con esta división, puede encontrar que las únicas razones por las que el ahorro podría fallar son 1) viola restricciones de unicidad, o 2) algo catastrófico. A continuación, puede devolver falso para el primer caso y lanzar una excepción para el otro.

3

Los únicos puntos importantes son:

  • Desde la perspectiva del front-end (s), la etapa intermedia debe realizar toda la validación, nunca se sabe si alguien va a intentar eludir su frente -fin de la validación de hablar directamente a su etapa intermedia (por cualquier razón)
  • la etapa intermedia puede optar por delegar parte de que la validación de la capa de base de datos (por ejemplo, restricciones de integridad de datos)
  • Usted puede duplicar opcionalmente una validación en el UI, pero eso solo debería ser para en aras del rendimiento (para evitar viajes redondos al Nivel Medio para escenarios comunes, como campos obligatorios faltantes, datos formateados incorrectamente, etc.) Estos controles deben nunca tomar el lugar de hacerlos en el Nivel Medio
4

Simplemente un comentario rápido (y con suerte útil): cuando se pregunte dónde colocar la validación, intente simular que, pronto, va a recrear completamente su capa de interfaz de usuario con una tecnología con la que aún no está familiarizado **. Trate de mantener fuera de esa capa cualquier lógica comercial similar a la validación que sepa con certeza que tendrá que volver a escribir en la nueva tecnología.

Encontrará excepciones: lógica empresarial que termina en la capa de la interfaz de usuario independientemente, pero de todos modos es una consideración útil.

** Mobile Dev, Silverlight, Voice XML, lo que sea - pretender que no conoce la tecnología de su "nueva" capa de interfaz de usuario le ayuda a abstraer sus preocupaciones y a enrutarse menos en los detalles de implementación.

1

Si el nivel R está más cerca del usuario (o cualquier flujo de entrada que no controle) que el nivel S, el nivel S debe validar todos los datos recibidos del nivel R. Esto no significa que el nivel R no valide datos . Es mejor para el usuario si la GUI le advierte que está cometiendo un error antes de intentar una nueva transacción. Pero no importa cuán a prueba de balas sea la validación en su GUI, el siguiente nivel no debe confiar en que haya tenido lugar ninguna validación.

Esto supone que su base de datos está completamente bajo su control. Si no, tienes problemas mayores.

1

Además, podría hacer que la IU pase los datos necesarios para construir un objeto Person a través de algún tipo de objeto PersonBuilder, para que la creación de objetos se consolide en la capa dominio/negocio, y pueda mantener el objeto Person en un estado eso siempre es consistente Esto tiene más sentido para las entidades más complejas, sin embargo, incluso para las simples, es bueno centralizar la creación de objetos, al igual que centralizar la persistencia, etc.

2

La validación debe hacerse en los tres niveles.

Cuando estoy en un proyecto, supongo que estoy haciendo un marco, que la mayoría de las veces no es el caso.Cada capa está separada y debe verificar todas las capas de entrada antes de realizar una operación

Cada nivel puede tener una forma diferente de hacerlo, no es necesario que todos usen el mismo, pero idealmente todos deben usar la misma validación con el capacidad de personalizarlo.

Nunca querrá dejar datos erróneos en la base de datos. Por lo tanto, nunca puede confiar en los datos que obtiene de la capa empresarial. Necesita ser verificado.

En la capa empresarial, nunca puede confiar en la capa de interfaz de usuario, y debe verificarla para evitar llamadas innecesarias a la capa de la base de datos. La capa UI funciona de la misma manera.

2

No estoy de acuerdo con el comentario de David Basarab de que las mismas validaciones deberían estar presentes en todas las capas. Esto desafía el paradigma de la responsabilidad de las capas por una razón. En segundo lugar, aunque la intención principal es hacer que las capas (o componentes) estén ligeramente acopladas, también es importante que se otorgue un nivel de responsabilidad (y, por lo tanto, de confianza) en las capas. Aunque podría ser necesario duplicar algunas validaciones en UI y Business Layer (ya que la capa de IU se puede eludir mediante intentos de piratería), sin embargo, no es recomendable repetir las validaciones en cada capa. Cada capa debe realizar solo aquellas validaciones de las que son responsables. El mayor defecto en la repetición de validaciones en todas las capas es la redundancia de código, que puede causar pesadillas de mantenimiento.

Cuestiones relacionadas