Gestion des erreurs et des commentaires lors de l'exécution des opérations en vrac dans une architecture à plusieurs niveaux

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

Question

Disons que vous avez une méthode de logique métier qui peut effectuer une certaine opération sur un certain nombre d'objets. Peut-être que vous souhaitez appeler un service Web de numéro de loterie, une fois pour chaque personne sélectionnée dans une liste. En Java, le code pourrait ressembler à ceci:

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

Notez que le service Web de numéro de loterie peut avoir des effets secondaires, comme l'enregistrement que la personne a demandé un numéro de loterie (peut-être facturer son compte), donc même si l'appel de service Web échoue pour une personne, il a peut-être réussi pour d'autres. Ces informations (les numéros de loterie) devront être renvoyées à un niveau supérieur (la vue).

S'il s'agissait d'un cas où une seule opération a eu lieu, la méthode de la logique métier pourrait renvoyer une seule valeur (par exemple, le numéro de loterie) ou lancer une exception avec tous les détails de l'échec. Mais pour les opérations en vrac, il serait possible que quelques-unes des opérations réussissent et quelques-unes à échouer.

Cela ressemble à un type de problème qui se produirait dans de nombreuses applications et il devrait y avoir un moyen propre de le gérer. Alors, quelle est la meilleure façon de renvoyer ce type d'informations de la couche logique métier à une autre couche dans une application (comme une vue), de préférence d'une manière générique qui peut être réutilisée pour différents types de données et d'opérations?

Était-ce utile?

La solution

Si je comprends, vous avez une situation où certaines demandes peuvent passer et certaines peuvent échouer. Je ne sais pas où vous voulez que l'erreur revienne, mais vous pourriez avoir l'un des éléments suivants (ou une variante, ou une combinaison):

  • Une liste d'erreurs et les objets de domaine affectés. Un objet de domaine de base ou quelque chose avec un ID persistant peut être utile pour la réutilisation. Par exemple, une collection d'erreurs se référant aux objets de domaine.
  • Vous pouvez injecter (AOP, DI) dans l'objet de personne une sorte d'objet / message d'erreur. Par exemple si (personne. Erreurs) {...}
  • Vous pouvez envelopper la collection de personnes dans un message avec en-tête, corps, informations d'erreur
  • Tous vos objets de domaine peuvent inclure une collection d'erreurs accessible via une interface; ou la personne prend en charge l'interface ihaserrors. Vous pouvez faire de cette générique et utiliser un objet d'erreur de base prenant en charge les avertissements et la validation et toutes sortes de choses.

Si vous êtes dans un véritable système à plusieurs niveaux (plutôt qu'en couches), vous pouvez avoir une architecture basée sur un message qui pourrait facilement accueillir une sorte de mécanisme générique d'erreur / d'avertissement / validation. Les systèmes SOA / AJAX se prêtent à cela.

Heureux de plonger un peu plus profondément si vous avez des questions spécifiques.

Autres conseils

Cette question met en évidence des différences importantes entre les utilisations appropriées de la gestion des exceptions, des transactions et l'idée Flux de travail "Compensation" C'est ce que le demandeur essaie d'obtenir, lorsqu'il indique correctement:

Cela ressemble à un type de problème qui se produirait dans de nombreuses applications et il devrait y avoir un moyen propre de le gérer.

C'est un problème courant, d'abord quelques antécédents sur l'approche transactionnelle que vous essayez actuellement:

Les transactions de données ont été à l'origine modélisées après la comptabilité à double entrée - un seul le crédit et un correspondant débit a dû être enregistré ensemble ou pas du tout. À mesure que les transactions deviennent plus grandes que cela, ils deviennent de plus en plus problématiques à mettre en œuvre correctement et plus difficiles à faire face à un échec. Au fur et à mesure que vous commencez à porter l'idée d'une seule transaction à travers les limites du système, vous vous abordez très probablement. Cela peut être fait, mais nécessite des coordinateurs de transactions complexes et nécessairement à latence plus élevée. À une certaine échelle, les transactions sont la mauvaise mentalité et la compensation commence à avoir beaucoup plus de sens.

C'est là que vous revenez en arrière et regardez ce que fait réellement l'entreprise. Une seule grande transaction n'est probablement pas la façon dont les hommes d'affaires le voient. Habituellement, ils voient une étape terminée et, selon les résultats ultérieurs, différentes actions pour compenser peuvent être nécessaires. C'est là que l'idée d'un flux de travail et compensation entre. Voici une introduction à ces concepts

Par exemple, si vous commandez un livre sur Amazon, ils ne "verrouillent" pas le dossier pendant qu'il se trouve dans votre panier, ou même utilisent des transactions strictes pour déterminer si le livre est toujours en stock lorsque la commande est confirmée. Ils vous le vendront de toute façon et l'expédieront quand ils le pourront. S'ils n'ont pas réussi à l'obtenir en stock dans quelques semaines, ils vous enverront probablement un e-mail vous indiquant qu'ils essaient de répondre à vos besoins, et vous pouvez continuer à attendre qu'ils l'obtiennent en stock, ou vous peut annuler votre commande. C'est ce qu'on appelle la rémunération et est nécessaire dans de nombreux processus métier du monde réel.

Enfin, il y a rien d'exceptionnel à propos de tout cela. Attendez-vous à ce que cela puisse se produire et utiliser un flux de contrôle normal. Vous ne devez pas utiliser les fonctionnalités de gestion des exceptions de votre langue ici (Bonnes règles pour quand lancer une exception). Vous ne devez pas non plus compter sur les mécanismes spécifiques à l'outil (WCF?) Pour voir ou gérer des exceptions qui se produisent dans la mise en œuvre du service. La communication des défauts doit faire partie de votre contrat de données (contrats de défaut).

Malheureusement, par "la manière propre de le gérer", il n'y a pas de drapeau à définir qui s'occupera comme par magie, vous devez continuer à décomposer le problème et à gérer toutes les pièces résultantes. J'espère que ces concepts vous connecteront avec ce que les autres ont fait lorsqu'ils traitent de ce problème.

Sommaire:

  • Votre problème a dépassé le concept d'une transaction -> examiner la rémunération du flux de travail.

Bonne chance -

Je préférerais renvoyer une collection d'objets d'erreur personnalisés identifiant l'objet, qui est effectué par l'erreur, le code d'erreur et la description. De cette façon, les erreurs peuvent être essayées ou affichées plus loin à l'utilisateur.

Je pense que vous trop, utilisez des exceptions si vous pensez en ces termes!

Il est parfaitement acceptable de renvoyer des valeurs qui signifient l'échec, plutôt que de lancer une exception. C'est souvent mieux. Les exceptions sont mieux utilisées lorsque vous ne pouvez pas récupérer au niveau d'abstraction dans lequel vous vous trouvez, mais vous ne devriez pas les utiliser comme principal moyen de contrôle ou vos programmes deviendront très difficiles à lire.

Le service Web ne renvoie pas d'exceptions, il renvoie les codes de retour et les messages. Je stockerais une représentation utile qui présente les informations renvoyées et retournerais la liste de celles-ci pour la vue ou tout ce qui va la regarder.

Idéalement, l'appel à votre service Web devrait être comme ça.

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

Faire un appel de service Web pour chaque personne coûte cher. À l'extrémité du serveur, vous devez itérer la liste et effectuer l'opération. Le code côté serveur peut lancer 2 exceptions: BulkOperationFailedException - S'il y a une erreur fatale due au fichier DB Down ou Config. Traitement ultérieur non possible. BulkOperationException - Cela contient un éventail d'exceptions relatives à une personne. Vous pouvez persister un ID pour vous référer de manière unique à chaque objet. Votre code sera comme ceci:

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

Le fonctionnement est considéré comme un succès lorsqu'aucune exception n'est lancée. Vous pouvez avoir des codes d'erreur personnalisés pour les échecs au lieu d'utiliser des exceptions définies par l'utilisateur. La construction de la conception de l'opération BulkOperation dans le côté du serveur est délicate. Deuxièmement, vous devez classer les erreurs jetées du côté serveur dans BulkOperationFailedException et BulkOperationException. C'est comme ça que j'avais traité dans l'un de mes projets

Je regarderais dans DTOS pour ce type de tâche. Le DTO pourrait également inclure des informations sur le fait que le persistance a réussi ou non et d'autres types de "métadonnées".

Je retournerais probablement une carte de résultats de type Map<Person,Future<String>> de mon getLotteryNumbers<Collection<Person>> service.

J'allais ensuite parcourir la carte et j'utiliserais le Future.get() Pour obtenir le numéro de loterie ou l'exception lancée.

Dans certains de mes services, j'aime implémenter tous les appels en tant qu'appels à élément unique, puis j'ai une logique dans mon service pour les regrouper et les traiter en groupe. Le lot est implémenté en utilisant un LinkedBlockingQueue et un fil de sondage.

Dans ce scénario, je retourne un Future<Thing> qui attend que les résultats du lot soient disponibles en utilisant un CountdownLatch.

Jetez un œil à la concurrence de Java dans la pratique pour voir comment ces composants peuvent tous fonctionner ensemble http://jcip.net/

Une autre façon, en particulier pour les systèmes à haut débit, serait d'utiliser une conception basée sur des files d'attente où une entité de traitement effectuerait des opérations sur un objet, puis sur les résultats, mettrait l'objet dans différentes files d'attente pour un traitement supplémentaire par d'autres entités, puis de passer . Cela réduirait les goulots d'étranglement qui surgiraient en raison d'un traitement supplémentaire qui serait nécessaire, par exemple le traitement de la commande pour les produits en rupture de stock

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top