CRUD Class Library Design, para devolver mensajes útiles sobre fallas de lógica de negocios, eso no es excepcional

StackOverflow https://stackoverflow.com/questions/1632440

  •  06-07-2019
  •  | 
  •  

Pregunta

Estoy construyendo una biblioteca CRUD básica, que preveo usar en entornos locales (agregar referencia) y wcf (agregar referencia de servicio).

¿Cuáles son los mejores tipos de retorno para las partes Crear, Actualizar y Eliminar (que tienen reglas comerciales más complejas) de una configuración CRUD?

Quiero poder minimizar los cambios en el cable, pero también quiero proporcionar a mis clientes información significativa sobre cuándo una operación falla mi lógica comercial, pero es técnicamente válida (por lo tanto, no es una excepción) .

Tomemos, por ejemplo, el CRUD es para una clase de persona, que tiene estos campos: nombre, segundo nombre, apellido y fecha de Brith. Primero, Último y DOB son obligatorios, pero Medio no.

¿Cómo debo transmitir los fallos de la lógica empresarial al cliente? ES DECIR. " Debe especificar un valor para FirstName. "

  1. ¿Es aquí donde debería estar tirando excepciones? (no se siente como un caso excepcional, así que creo no, pero podría estar equivocado).
  2. ¿Debo usar void y un " out " ¿parámetro? Si es así, ¿de qué tipo debería ser?
  3. ¿Debo usar un tipo de retorno de objeto y poner datos allí sobre lo que sucede?
  4. ¿Alguna otra opción que me haya perdido por completo?
¿Fue útil?

Solución

  

1. ¿Es aquí donde debería estar lanzando excepciones? (no parece un caso excepcional, así que creo que no, pero podría estar equivocado).

Personalmente, creo que debe devolver un objeto con un resultado, así como cualquier error de validación, y no lanzar una excepción para la validación de datos, ya sea debido a falta de información (validación de formato) o validación de lógica de negocios. Sin embargo, sugiero lanzar una excepción para los errores que no están relacionados con los datos en sí, es decir: si la confirmación de la base de datos falla con datos válidos, etc.

Mi opinión aquí es que el fallo de validación no es una "ocurrencia excepcional". Personalmente, creo que cualquier cosa que un usuario pueda estropear simplemente no ingresando datos suficientes / correctos / etc. es algo que no es excepcional: es una práctica estándar y la API debe manejarlo directamente.

Las cosas que no están relacionadas con lo que está haciendo el usuario (es decir, problemas de red, problemas del servidor, etc.) son acontecimientos excepcionales y garantizan una excepción.

  

2. ¿Debería usar void y an " out " ¿parámetro? Si es así, ¿de qué tipo debería ser?

     

3. ¿Debo usar un tipo de retorno de objeto y poner datos allí sobre lo que sucede?

Personalmente prefiero la tercera opción. " fuera " Los parámetros no son muy significativos. Además, querrá devolver más de una sola información de estado de esta llamada; querrá devolver suficiente información para marcar las propiedades apropiadas como no válidas, así como cualquier información completa de toda la operación.

Esto probablemente requerirá una clase que contenga, como mínimo, un estado de confirmación (éxito / formato fallido / lógica de negocios fallida / etc.), una lista de asignaciones para propiedades - > errores (es decir: información de estilo IDataErrorInfo ), y potencialmente una lista de errores que no existen No está vinculado a una propiedad específica, sino que trata con la lógica de negocios de la operación como un todo, o la combinación de valores de propiedad sugeridos.

  

4. ¿Alguna otra opción que me haya perdido por completo?

La otra opción, que me gusta bastante, es tener la validación en un ensamblaje separado de la capa de procesamiento comercial. Esto le permite reutilizar la lógica de validación en el lado del cliente.

Lo bueno de esto es que puede simplificar y reducir drásticamente el tráfico de red. El cliente puede validar previamente la información y solo enviar datos a través del cable si es válida.

El servidor puede recibir los datos correctos, revalidarlos y devolver nada más que un solo resultado de confirmación. Creo que esto debería tener al menos tres respuestas: éxito, error debido a la lógica de negocios o error debido al formato. Esto proporciona la seguridad (no tiene que confiar en el cliente), y le brinda información sobre lo que no se está manejando correctamente, pero evita pasar información incorrecta del cliente > server y la información de validación del servidor > client , por lo que puede reducir drásticamente el tráfico.

La capa de validación puede (de forma segura) enviar la información a la capa CRUD para enviarla.

Otros consejos

Por razones de rendimiento, la validación de entrada inicial debe realizarse en su nivel de cliente. (es decir: no se moleste en enviar datos incorrectos por el cable). En el nivel de cliente, un error de validación de entrada sería, de hecho, un problema muy esperado que no debería arrojar excepciones. Sin embargo, si pones esto " temprano " validación en el nivel del cliente, una falla de validación de datos encontrada en cualquier nivel más profundo podría verse razonablemente como un problema inesperado, por lo que arrojar excepciones para los errores de validación de datos en esos niveles no sería inapropiado.

Puede encontrar interesante esta publicación de blog de Rob Bagby; describe cómo implementar un repositorio para manejar las operaciones CRUD y, a su punto, específicamente cómo implementar la validación, devolviendo una colección de "RuleViolation". al cliente en caso de que haya un problema.

http: // www.robbagby.com/silverlight/patterns-based-silverlight-development-part-ii-repository-and-validation/

[editar] Para mí, es un caso para lanzar una excepción: si crear un usuario necesita un nombre y no se proporciona el nombre, la persona que llama no ha utilizado los argumentos adecuados y no está utilizando el método en la forma prevista. InvalidArgumentException suena adecuado.

Realmente deberías revisar la implementación de las Reglas de Validación de Rocky Lhotka en su marco CSLA .

NOTA: no dije que usara su marco en masa, ya que tiene algunos problemas de acoplamiento que rompen algunos esfuerzos de SRP en las últimas tendencias de desarrollo de .NET.

Pero, su marco hace uso de "automático" notificación hasta la capa de IU e integración con mensajes de error de validación con soporte para controles Web / Winforms.

Editar: debo tener en cuenta que normalmente tendría los elementos de validación abstraídos en una capa empresarial, que manejaría la validación y llamaría a los métodos CRUD después de que la validación fue exitosa.

Una forma de hacerlo sería devolver una " respuesta " clase que contiene toda la información que un consumidor de su biblioteca necesitaría para evaluar lo que sucedió y qué hacer a continuación. Un ejemplo muy básico de una clase que podría usar sería algo como esto:

public class Response<T> where T:BusinessObject
{
    public Response(T oldOriginalValue, T newValue)
    {
    }

    /// <summary>
    /// List of Validation Messages
    /// </summary>
    public List<ValidationMessage> ValidationMessages { get; set; }

    /// <summary>
    /// Object passed into the CRUD method
    /// </summary>
    public T OldValue { get; private set; }

    /// <summary>
    /// Object passed back from the CRUD method, with values of identity fields, etc populated
    /// </summary>
    public T NewValue { get; private set; }

    /// <summary>
    /// Was the operation successful
    /// </summary>
    public bool Success { get; set; }
}

public class ValidationMessage
{
    /// <summary>
    /// Property causing validation message (i.e. FirstName)
    /// </summary>
    string Property { get; set; }

    /// <summary>
    /// Validation Message text
    /// </summary>
    string Message { get; set; }
}

El debate entre devolver una estructura de resultados con detalles de la falla versus lanzar una excepción se puede generalizar a "¿Puedo realizar con éxito la acción solicitada?" Está diseñando una biblioteca y cómo la biblioteca comunica los modos de falla es parte de eso.

Si se le pide a la biblioteca que cree un objeto Usuario pero no puede porque el nombre de usuario falló en la validación, puede devolver un Usuario vacío junto con los mensajes de falla de validación y esperar que el código del cliente pruebe el valor de retorno. Mi experiencia (DCOM pre flashbacks ATL) con tener que probar los valores de retorno para los códigos de error es que es fácil ser complaciente (o vago) y omitirlo.

Por lo que ha descrito, el uso de excepciones no está descartado. Defina una excepción principal para todos los tipos de excepciones que puede lanzar su biblioteca. De esa forma, los clientes no tienen que incluir una gran lista de sub-excepciones a menos que realmente quieran. Un ejemplo de esto es SqlException.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top