Contattaci

Exception vs Result

exception vs result
  • Data: 17 Gennaio 2022
  • Autore: Gabriele Seroni
  • Categorie

  • Giuneco Tech

    Il nostro settore richiede uno studio continuo e una forte attenzione all’innovazione. Incentiviamo quindi dei programmi formativi annuali per tutti i componenti del team, con ore dedicate (durante l’orario di lavoro) e serate formative sia online che in presenza. Sponsorizziamo eventi, sia come partner che semplicemente come partecipanti, e scriviamo articoli su quello che abbiamo imparato per essere, a nostra volta, dei divulgatori.

    Vai alla sezione Team
  • Exception vs Result

    Stimolato dal recente articolo redatto da Federico Barsotti, riguardante il pacchetto nuget chiamato FluentResult, ho deciso di produrre questa breve riflessione sulla differenza tra “validazione” e “invarianze”.

    Riassumendo l’articolo sopracitato, viene presentata una libreria che permette, in maniera molto comoda ed intuitiva, di utilizzare una classe “Result” come tipo di output per decorare i risultati dei nostri metodi con informazioni o metadati utili al chiamante (in special modo per gestire errori).

    Da qui in poi userò il termine Result per racchiudere tutti quei modi di ottenere informazioni sul successo o fallimento di un metodo al chiamante basati su valori in output (codice di result, bool ecc…)

    In un mio vecchio articolo riguardante il “Clean code”, ho sostenuto, quasi in un apparente contrasto con l’approccio descritto da Federico, come l’utilizzo dei “Result” in maniera indiscriminata offuscasse la logica applicativa del nostro codice, obbligando i client di un determinato metodo a congestionare di IF la propria logica in ricerca di situazioni di errore (o di successo).

    Ho sottolineato, inoltre, come lasciare ai client l’onere di gestire i Result contenenti errori, avesse una non trascurabile probabilità di portare a situazioni di inconsistenza e/o incorrettezza dei dati:

    cosa può succedere se ci si dimentica l’ennesimo IF annidato?

    I due articoli sembrano suggerire approcci incompatibili e strade parallele, in realtà quello che voglio sostenere qui è esattamente l’opposto: i due approcci non solo sono perfettamente compatibili, ma il loro connubio è spesso un approccio pragmatico da usare in molte situazioni di uso comune.

    Cosa è un caso eccezionale?

    Spesso quello che si legge in libri o articoli, quando si pone la domanda se sia più corretto usare Result o Eccezioni, è (sintetizzando molto):

    “Usate le eccezioni per situazione eccezionali, per il resto usate Result”.

    Già in una definizione del genere possiamo intuire che “l’approccio ibrido” è quello, in qualche modo, condiviso da molti esperti.

    Il problema di questa definizione è che manca di specificare cosa sia una situazione eccezionale, lasciandoci vulnerabili di fronte a domande del genere:

    • La perdita di connettività verso il database è una situazione eccezionale?
    • Una email che non ha la @ è eccezionale?
    • Gli input incoerenti inseriti in un form da un utente sono eccezionali?

     

    Per cercare di dare una definizione migliore della precedente, prendiamo una semplificazione di una Onion Architecture standard:

     

    https://enterprisecraftsmanship.com/posts/always-valid-domain-model/

    In questa immagine abbiamo la suddivisione in 2 layers dell’architettura:

    1. Domain model => qui risiedono le invarianze
    2. Application services => qui risiedono le validazioni

    Nel Domain model risiedono tutte le Entity, Value object, Aggregates mentre degli Application Services fanno parte tutti quei moduli (classi, librerie, framework) che ci permettono di relazionarci con il mondo esterno come Controllers/Repository ecc.

    Gli approfondimenti su Onion Architecture o sul Domain Driven Design esulano dallo scope di questo articolo. I link per l’approfondimento sono nell’appendice.

    Validations vs Invariants

    Un errore concettuale, prima ancora che pratico, è quello di pensare che le regole di validazione e le invarianze dei nostri modelli siano per qualche motivo identità diverse.

    Non è così.

    Sia le regole di validazione che le invarianze sono incarnazioni concrete delle regole di business sulle quali si basa la nostra applicazione.

    Ad esempio, validare, in un Controller, che l’età di un utente non sia negativa quando inviata da un form di registrazione, ha senso perché esiste una regola di business di tipo “gli utenti non possono avere età negative” e conseguentemente esisterà una invarianza nel modello di dominio Utente che non permetterà di crearlo qualora arrivi una data di nascita negativa.

    Per questo validazione ed invarianze sono due lati della stessa medaglia. La medaglia è la business rule.

    Ma se validazione e invarianze sono, praticamente, 2 incarnazioni dello stesso concetto, perché ha senso averle entrambe e non solo una?

    La risposta è, appunto, il loro rapporto con la eccezionalità:

    la violazione delle regole di business porta ad una situazione eccezionale se avviene all’interno del nostro Domain Model (strato applicativo dove dobbiamo garantire la correttezza e la consistenza dei nostri oggetti), mentre è perfettamente lecito, e per nulla eccezionale, che ci sia incorrettezza in dati inviati da un client esterno (che sia un ws, un db condiviso, la compilazione manuale di un form).

    Questa immagine rende molto bene l’idea:

    In conclusione

    Nello strato degli Application Services avviene la validazione di input dal mondo esterno ed in questo strato è saggio ed utile fornire informazioni precise ad un client, così da permettergli di correggere in maniera comoda ed intuitiva i dati inviati.

    È qui che sono estremamente utili librerie come quella proposta da Federico.

    Nello strato di Domain, dove i dati in input devono aver già passato la fase di validazione attraverso lo strato di servizi applicativi, l’arrivo di input che non soddisfano le invarianze è da considerarsi una situazione eccezionale ed il lancio di eccezioni è lo strumento che più si addice.

    Quando si ha un mancato soddisfacimento delle invarianze a livello di dominio l’applicazione è in una situazione irreversibile, non può continuare in nessun modo. Non usare le eccezioni, ma codici di errore qui, è estremamente pericoloso: se un client si dimenticasse di controllare il Result e continuasse l’esecuzione rischierebbe di corrompere i dati in maniera irreversibile!

    L’eccezione garantisce come comportamento “di default” l’arresto dell’esecuzione (bisogna catcharla esplicitamente se si sa come affrontarla), mentre il Result ha il comportamento opposto (si deve controllare il codice di errore per bloccare l’esecuzione).