2009-08-27 11 views
11

Digamos que tiene un método de lógica de negocios que puede realizar algunas operaciones en varios objetos. Tal vez desee llamar a un servicio web de selección de números de lotería, una vez por cada persona seleccionada de una lista. En Java, el código podría ser algo como esto:Manejo de errores y comentarios al realizar operaciones masivas en una arquitectura de varios niveles

Set<Person> selectedPeople = ... // fetch list of people 
for (Person person : selectedPeople) { 
    String lotteryNumber = callLotteryNumberWebService(person); 
    // ... 
} 

Tenga en cuenta que el número de lotería servicio web puede tener efectos secundarios, como la grabación de que la persona que ha solicitado un número de lotería (quizás carga su cuenta), por lo que incluso si la llamada al servicio web falla para una persona, puede haber tenido éxito para otros. Esta información (los números de la lotería) deberá enviarse a un nivel superior (la vista).

Si este fuera el caso donde se realizó una sola operación, el método de lógica de negocios podría devolver un único valor (por ejemplo, el número de lotería) o arrojar una excepción con cualquier detalle de la falla. Pero para operaciones masivas, algunas de las operaciones podrían tener éxito y algunas podrían fallar.

Esto parece ser un tipo de problema que ocurriría en muchas aplicaciones y debería haber una manera limpia de manejarlo. Entonces, ¿cuál es la mejor manera de devolver este tipo de información desde la capa de lógica de negocios a otra capa en una aplicación (como una vista), preferiblemente de una manera genérica que puede reutilizarse para diferentes tipos de datos y operaciones?

Respuesta

1

Si entiendo, tiene una situación donde algunas solicitudes pueden pasar y algunas pueden fallar. No estoy seguro de dónde quiere que vuelva el error, pero podría tener uno de los siguientes (o una variante, o una combinación):

  • Una lista de errores y los objetos de dominio afectados. Un objeto de dominio base o algo con una ID persistente puede ser útil para su reutilización. P.ej. una colección de errores que hacen referencia a objetos de dominio.
  • Puede inyectar (AOP, DI) en el objeto Persona algún tipo de objeto/mensaje de error. P.ej. if (persona Errores) {...}
  • Puede envolver la colección Person en un mensaje con encabezado, cuerpo, información de error
  • Todos sus objetos de dominio pueden incluir una colección de errores accesible a través de una interfaz; o Persona admite la interfaz IHasErrors. Podrías hacer esto genérico y usar un objeto base de error que admita advertencias y validación y todo tipo de cosas.

Si se encuentra en un sistema genuino de varios niveles (en lugar de en capas), entonces puede tener una arquitectura basada en mensajes que fácilmente podría acomodar algún tipo de mecanismo genérico de error/advertencia/validación. Los sistemas SOA/Ajax se prestan a esto.

Feliz de profundizar un poco más si tiene algunas preguntas específicas.

1

Preferiría devolver una colección de objetos de error personalizados que identifiquen el objeto, que se ve afectado por el error, el código de error y la descripción. De esta forma, los errores pueden intentar remediarse o mostrarse más al usuario.

0

Probablemente devolveré un mapa resultante del tipo Map<Person,Future<String>> de mi servicio getLotteryNumbers<Collection<Person>>.

Me gustaría iterar a través del mapa y usar el Future.get() para obtener el número de lotería o la excepción lanzada.

En algunos de mis servicios, me gusta implementar todas las llamadas como llamadas de un solo elemento y luego tener lógica en mi servicio para procesarlas y procesarlas como un grupo. El procesamiento por lotes se implementa utilizando un LinkedBlockingQueue y un hilo de sondeo.

En este escenario, devuelvo un Future<Thing> que espera a que los resultados del lote estén disponibles utilizando un CountdownLatch.

Tome un vistazo a Java concurrencia en la práctica para ver cómo estos componentes pueden trabajar todos juntos http://jcip.net/

0

Me miro en DTOs para este tipo de tarea. El DTO también podría incluir información sobre si la persistencia fue exitosa o no y otros tipos de "metadatos".

1

¡Creo que realmente está abusando de las excepciones si está pensando en estos términos!

Está perfectamente bien devolver valores que significan falla, en lugar de arrojar una excepción. A menudo es mejor Las excepciones se usan mejor cuando no se puede recuperar en el nivel de abstracción en el que se encuentra, pero no debe usarlas como el principal medio de flujo de control o sus programas serán muy difíciles de leer.

El servicio web no devuelve excepciones, devuelve códigos de retorno y mensajes. Guardaría alguna representación útil que presente la información devuelta, y devolveré la lista de aquellos para la vista o lo que sea que vaya a verla.

10

Esta cuestión pone de manifiesto importantes diferencias entre los usos adecuados de manejo de excepciones, las transacciones y la idea flujo de trabajo "compensación" que es lo que el autor de la pregunta está tratando de llegar a, cuando correctamente indicando:

Esto parece como un tipo de problema que ocurriría en muchas aplicaciones y debería haber una manera limpia de manejarlo.

Es un problema común, en primer lugar algunos antecedentes sobre el enfoque transaccional que está intentando actualmente:

transacciones de datos se modelaron inicialmente después de contabilidad de doble entrada - una sola de crédito y una de débito correspondiente tuvieron que grabarse juntos o no grabarse. A medida que las transacciones se vuelven más grandes, se vuelven cada vez más problemáticas para implementarse correctamente y es más difícil lidiar con una falla. A medida que comienzas a llevar la idea de una transacción única a través de los límites del sistema, lo más probable es que lo estés abordando mal. Se puede hacer, pero requiere coordinadores de transacción complejos y necesariamente de latencia más alta. En una escala determinada, las transacciones son una mentalidad equivocada, y la compensación comienza a tener mucho más sentido.

Aquí es donde puede volver atrás y observar lo que hace realmente el negocio. Una gran transacción individual probablemente no sea la que la gente de negocios vea. Por lo general, ven un paso completado, y dependiendo de los resultados posteriores, pueden ser necesarias diferentes acciones para compensar. Aquí es donde la idea de una compensación de flujo de trabajo y entra en acción. Here's one introduction to those concepts

Por ejemplo, si usted pide un libro de Amazon, es probable que no "bloquear" el registro mientras se encuentra en su carrito de compras, o incluso utilice transacciones estrictas para determinar si el libro aún está en stock cuando se confirme el pedido. Se lo venderán de todos modos, y lo enviarán cuando puedan. Si no lograron ponerlo en stock en unas pocas semanas, probablemente le envíen un correo electrónico indicándole que intentan satisfacer sus necesidades, y usted puede seguir esperando que lo almacenen, o usted puede cancelar su pedido Esto se llama compensación y es necesario en muchos procesos comerciales del mundo real.

Por último, no es nada excepcional acerca de esto. Espere que esto pueda suceder y use un flujo de control normal. No debe usar las funciones de manejo de excepciones de su idioma aquí (good rules for when to throw an exception). Tampoco debe confiar en los mecanismos específicos de la herramienta (WCF, por sus siglas en inglés) para ver o manejar las excepciones que ocurren dentro de la implementación del servicio. Las fallas de comunicación deben ser una parte normal de su contrato de datos (contratos de falla).

Desafortunadamente, por "una manera limpia de manejarlo" no hay una bandera que establecer que mágicamente se encargará de ello, usted tiene que seguir descomponiendo el problema y tratar con todas las piezas resultantes. Con suerte, estos conceptos lo conectarán con lo que otras personas han hecho al abordar este problema.

Resumen:

  • Su problema se ha superado el concepto de una transacción -> a ver en la compensación del flujo de trabajo.

Buena suerte -

+0

¡Excelente respuesta! –

0

Otra forma, especialmente para sistemas de alto rendimiento, sería el uso de un diseño basado en colas en las que una entidad de procesamiento sería realizar operaciones en un objeto y luego sobre la base de los resultados de poner el objeto en diferentes colas para el procesamiento adicional por otras entidades y luego seguir adelante. Esto reduciría los cuellos de botella que surgirían debido al procesamiento adicional que se requeriría, p. procesamiento de pedido para productos fuera de stock

0

Idealmente, la llamada a su servicio web debería ser así.

List<Person> selectedPeople = ... //fetch list of people 
callLotteryNumberWebService(selectedPeople.ToArray); 

Realizar un llamado al servicio web para cada persona es costoso. En el extremo del servidor, necesita iterar sobre la lista y realizar la operación. El código del lado del servidor puede arrojar 2 excepciones: BulkOperationFailedException - si hay un error fatal debido al archivo db down o config faltante. No es posible un procesamiento posterior. BulkOperationException: contiene una serie de excepciones relacionadas con una persona. Puede persistir una identificación para referirse de manera única a cada objeto. Su código será la siguiente:

List<Person> selectedPeople = ... // fetch list of people 

try{ 
    callLotteryNumberWebService(selectedPeople.ToArray); 
}catch (BulkOperationFailedException e) { 
    SOP("Some config file missing/db down.No person records processed") 
}catch(BulkOperationException e) { 
    UserDefinedExceptions us = e.getExceptions() 
    foreach(exception ein us) { 
     // read unique id to find which person object failed 
    } 
} 

construct msg based on which personobject succeeded and which failed 

operación se considera exitosa cuando no se lanzan excepciones. Puede tener códigos de error personalizados para fallas en lugar de usar excepciones definidas por el usuario. Construir la excepción BulkOperationException en el lado del servidor es complicado. En segundo lugar, debe categorizar los errores lanzados desde el servidor en BulkOperationFailedException y BulkOperationException. Así era como lo había manejado en uno de mis proyectos

Cuestiones relacionadas