La convalida deve essere eseguita il prima possibile.
La convalida in qualsiasi contesto, sia il modello di dominio o qualsiasi altro modo di scrivere software, dovrebbe servire allo scopo di COSA vuoi validare ea quale livello sei al momento.
Sulla base della tua domanda, immagino che la risposta sarebbe quella di dividere la convalida.
La convalida della proprietà verifica se il valore per quella proprietà è corretto, ad es. Quando si prevede un intervallo compreso tra 1 e 10.
La convalida dell'oggetto garantisce che tutte le proprietà sull'oggetto siano valide insieme. es. BeginDate è prima di EndDate. Supponiamo di leggere un valore dall'archivio dati e sia BeginDate che EndDate sono inizializzati su DateTime.Min per impostazione predefinita. Quando si imposta BeginDate, non vi è alcun motivo per imporre la regola "deve essere prima di EndDate", poiché questo non si applica ANCORA. Questa regola dovrebbe essere controllata DOPO che tutte le proprietà sono state impostate. Questo può essere chiamato a livello di radice aggregata
La convalida dovrebbe anche essere preformata sull'entità aggregata (o radice aggregata). Un oggetto Order può contenere dati validi e quindi OrderLines. Ma poi una regola commerciale afferma che nessun ordine può superare $ 1.000. Come fareste applicare questa regola in alcuni casi ciò è consentito. non puoi semplicemente aggiungere una proprietà "non convalidare l'importo" poiché ciò comporterebbe un abuso (prima o poi, forse anche tu, solo per togliere questa "cattiva richiesta").
poi c'è la validazione a livello di presentazione. Stai davvero per inviare l'oggetto sulla rete, sapendo che fallirà? O risparmierai all'utente questo peso e lo informerai non appena inserirà un valore non valido. ad esempio, la maggior parte delle volte il tuo ambiente DEV sarà più lento della produzione. Ti piacerebbe aspettare 30 secondi prima di essere informato di "hai dimenticato DI NUOVO questo campo durante ancora UN'ALTRA prova", specialmente quando c'è un bug di produzione da correggere con il tuo capo che ti respira al collo?
La convalida a livello di persistenza dovrebbe essere il più vicino possibile alla convalida del valore della proprietà. Ciò consentirà di evitare eccezioni con la lettura di errori "null" o "valore non valido" quando si utilizzano mapper di qualsiasi tipo o semplici lettori di dati vecchi. L'uso delle procedure memorizzate risolve questo problema, ma richiede di scrivere la stessa logica di valutazione ANCORA e di eseguirlo DI NUOVO. E le procedure memorizzate sono il dominio di amministrazione DB, quindi non tentare di fare anche il SUO lavoro (o peggio di disturbarlo con questo "prelibatezza che non viene pagato".
quindi per dirlo con alcune parole famose "dipende", ma almeno adesso sai PERCHÉ dipende.
Vorrei poter mettere tutto questo in un unico posto, ma sfortunatamente non è possibile farlo. Ciò porterebbe una dipendenza da un "oggetto God" contenente TUTTA la validazione per TUTTI i layer. Non vuoi percorrere quel sentiero oscuro.
Per questo motivo, lancio eccezioni di convalida solo a livello di proprietà. Tutti gli altri livelli che uso ValidationResult con un metodo IsValid per raccogliere tutte le "regole infrante" e passarle all'utente in una singola AggregateException.
Durante la propagazione dello stack di chiamate, le raccolgo nuovamente in AggregateExceptions fino a quando non raggiungo il livello di presentazione. Il livello di servizio può generare questa eccezione direttamente al client in caso di WCF come FaultException.
Questo mi permette di prendere l'eccezione e di dividerla per mostrare singoli errori in ciascun controllo di input o appiattirla e mostrarla in un unico elenco. La scelta è tua.
questo è il motivo per cui ho anche menzionato la convalida della presentazione, per cortocircuitare il più possibile.
Nel caso ti stia chiedendo perché ho anche la convalida a livello di aggregazione (o livello di servizio se vuoi), è perché non ho una sfera di cristallo che mi dice chi utilizzerà i miei servizi in futuro. Avrai abbastanza problemi a trovare i tuoi errori per impedire ad altri di commettere errori tuoi :) inserendo dati non validi. Amministri l'applicazione A, ma l'applicazione B fornisce alcuni dati utilizzando il tuo servizio. Indovina chi chiedono per primo quando c'è un bug? L'amministratore dell'applicazione B informerà felicemente l'utente "non c'è nessun errore da parte mia, ho solo inserito i dati".