Sto imparando la progettazione del software creando un'applicazione web CRUD (ASP.NET MVC con Entity Framework). L'ho diviso in due progetti: la prima è la libreria Core, che contiene la logica di business, la seconda è la Web GUI. Ho avuto un'idea, che la libreria Core separata può essere utilizzata altrove (ad esempio l'applicazione desktop) o la GUI può essere completamente cambiata in seguito, se necessario.
La logica aziendale è memorizzata in "gestori" che operano con entità di database e forniscono risultati utilizzando oggetti di trasferimento dati, perché non voglio esporre le entità db al di fuori del core. La GUI Web accetta l'input dell'utente e utilizza i manager per svolgere il lavoro. Definisce inoltre gli oggetti del modello di vista, che sono fondamentalmente copiati dagli oggetti di trasferimento dati, ma arricchiti con gli attributi di annotazione dei dati.
problema
Il progetto Web esegue la convalida lato server sugli oggetti del modello di visualizzazione, in quanto è necessario verificare l'input dell'utente. Il progetto principale esegue di nuovo la stessa convalida sugli oggetti di trasferimento dati, poiché non conosce nulla sullo strato della GUI sopra di esso. La domanda è come posso evitare la convalida dei dati duplicati, perché a volte richiede query al database e in generale aggiunge il sovraccarico e il codice duplicato. Quali sono le migliori pratiche nel mio caso? O l'idea di separare la soluzione in due progetti è sbagliata?
I post che ho letto finora discutono della validazione in normali soluzioni a 3 livelli, in cui le convalide sono leggermente diverse in ogni livello (client (javascript), business e livello dati) e tale convalida duplicata non è un problema (entrambi sono business) .
Soluzioni che ho trovato
- Consenti alla GUI di eseguire la convalida e di ignorarne qualsiasi in Core.
- Consenti ai gestori di eseguire la convalida e rilevare le eccezioni sollevate nella GUI. Ho letto che questa è una cattiva pratica [citation-needed] .
- Sposta la convalida all'interno degli oggetti di trasferimento dati con un flag "già validato" privato. Fornire ai manager il metodo ValidateDto che attiva la convalida su un determinato DTO. I risultati di convalida nel dizionario di messaggi di errore con nomi di proprietà come chiavi, imposta flag, ma se già impostato non fa nulla. Anche il meccanismo è necessario per rimuovere il flag se DTO è stato modificato.
Modifica
Penso di aver bisogno di chiarire: Core è una libreria di classi, il web è una normale applicazione ASP.NET MVC con viste, controller, ecc.
Modifica 2
La mia domanda sembra un duplicato di Validazione dell'input dei dati - Dove? Quanto? [chiuso] e non sono d'accordo. Questo post discute diversi ambiti di convalida su diversi livelli all'interno di un'applicazione (javascript lato client, livello aziendale, convalida livello dati). Ogni livello copre diversi ambiti di validazione, ma tendono ad intersecarsi. Mentre cerco una risposta su come rimuovere la convalida duplicata all'interno di un singolo livello (business) quando viene separata in due progetti. Anche i post collegati non hanno una risposta accettata e la risposta più votata sta fondamentalmente dicendo di spostare la convalida di base in un unico posto. Che è in definitiva corretto ma non è una risposta al mio problema.
Tutte le risposte finora suggeriscono di mettere la validazione nella libreria delle classi Core. Quindi la domanda ora è come dovrei restituire le informazioni sugli errori di input non validi.
-
Saranno metodi speciali in "API" core che dovrebbero essere chiamati ogni volta prima che i dati vengano forniti a Core. Questa API restituirà informazioni dettagliate sugli errori che possono essere fornite all'utente. Ciò richiede che l'utente della libreria Core (programmatore) conosca tale comportamento speciale (o un sacco di codice in caso contrario, per decidere se la convalida è stata chiamata dall'utente prima o meno).
-
Sarà un'eccezione appositamente definita che verrà lanciata da Core se l'input non è valido. Questa eccezione conterrà informazioni dettagliate sugli errori.
Soluzione
Grazie a tutti per le risposte e i commenti! Ho finito con la rimozione di tutte le convalide lato server dal progetto Web e la convalida solo all'interno del progetto Core. Ho anche creato InvalidModelException come utente Machado ha suggerito e utilizzato la proprietà Exception.Data per archiviare tutti gli errori del modello in forma di coppie chiave / valore, dove la chiave era il nome della proprietà non valida e il valore era il codice di errore precedentemente definito in Core come enum. All'interno del progetto Web sto rilevando questa eccezione InvalidModelException e una volta catturato sto riempiendo ModelState con errori di modello per fornire un feedback migliore all'utente. Sto memorizzando tutti i messaggi di errore facili da usare nel file .resx della risorsa nel progetto Web. In base al nome della proprietà e al codice di errore di InvalidModelException, sto generando la stringa ErrorMessageName per estrarre un messaggio di errore amichevole dal file di risorse.