Dove convalidare le regole del modello di dominio che dipendono dal contenuto del database?

10

Sto lavorando su un sistema che consente agli amministratori di definire i moduli che contengono campi. I moduli definiti vengono quindi utilizzati per immettere dati nel sistema. A volte i moduli sono compilati da un utente tramite una GUI, a volte il modulo viene compilato in base ai valori riportati da un altro sistema.

Per ciascun campo, l'amministratore può definire una regola di convalida che limita i valori consentiti per il campo. Le regole di convalida possono essere qualsiasi cosa, dal "valore inserito nel campo deve essere vero o falso" a "il valore inserito nel campo deve esistere nella colonna A della tabella B nel database". L'amministratore può in qualsiasi momento modificare la regola di convalida per il campo.

In questo scenario, quale a tuo parere è il posto più adatto per convalidare che ogni campo è compilato correttamente? Attualmente ho in mente due approcci principali:

Opzione n. 1: convalida nel modello di dominio

Ogni oggetto Field conterrà la regola di convalida specificata dall'amministratore. Gli oggetti Field avrebbero anche un riferimento a un IValidator. Quando viene effettuato un tentativo di impostare il valore del campo, il campo passerà il valore specificato e la regola di convalida a IValidator. Se il valore dato non è valido, una ValidationException verrebbe generata e gestita in modo appropriato nell'interfaccia utente / interfaccia con l'altro sistema.

Pro:

  • Protezione avanzata contro l'assegnazione accidentale di campi ai valori che violano la regola di convalida

Contro:

  • Il livello di accesso ai dati deve essere in grado di bypassare la convalida e costruire campi che violano la regola di convalida corrente. Nonostante l'amministratore abbia modificato la regola di convalida per un campo, dobbiamo comunque essere in grado di costruire oggetti Field in base ai vecchi dati, ad es. quando si esegue il rendering di un modulo compilato anni fa. Questo potrebbe potenzialmente essere risolto memorizzando la regola di convalida corrente ogni volta che memorizziamo il campo.

  • In questa progettazione, il modello Field ha un collegamento indiretto al livello / archivio di accesso ai dati tramite IValidator. L'iniezione di Servizi / Repository su Modelli di dominio sembra essere generalmente aggrottare le sopracciglia su .

Opzione n. 2: convalida in un servizio

Cerca di assicurarti che tutti i tentativi di impostare il valore di un campo passino attraverso un servizio che garantisce la validità della regola di convalida. Se viene violata la regola di convalida, genera una ValidationException.

Naturalmente, il livello di accesso ai dati non utilizzerà il servizio quando si creano oggetti Field precedentemente mantenuti nel DB.

Pro:

  • Non viola il "non iniettare servizi / repository nei tuoi modelli di dominio" - pensieroso.

  • Non è necessario mantenere la regola di convalida corrente quando si mantiene il campo. Il servizio può semplicemente cercare la regola di convalida corrente per il campo; quando guardi i dati storici, il valore del campo non verrà modificato.

Contro:

  • Non è garantito che tutta la logica che dovrebbe utilizzare il Servizio per impostare il valore Campo lo faccia effettivamente. Lo vedo come un grosso inconveniente; tutto ciò che sembra prendere è qualcuno che scrive "thisField.setValue (thatField.getValue ())" e la Validation Rule di thisField potrebbe essere violata senza che nessuno ne sia il più saggio. Questo potrebbe potenzialmente essere mitigato assicurandosi che il valore del campo corrisponda alla regola di convalida quando il livello di accesso ai dati sta per persistere nel campo.

Al momento preferisco l'opzione n. 1 rispetto all'opzione n. 2, principalmente perché la considero una logica aziendale e ritengo che l'opzione n. 2 rappresenti un rischio maggiore di introdurre dati errati nel sistema. Quale opzione preferisci o esiste un altro design che si adatti a questo scenario meglio delle due opzioni descritte?

Modifica (complessità delle convalide)

I casi di validazione che sono emersi per ora sono relativamente semplici; il valore del campo deve essere ad es. numerico, una data, una data con un orario o un valore esistente in una colonna del database. Tuttavia, sospetto che la complessità aumenti gradualmente nel tempo. Ad esempio, la soluzione di convalida deve essere costruita tenendo conto dell'internazionalizzazione: elementi come Date possono essere inseriti in una sintassi specifica della locale.

Ho deciso di procedere con l'Opzione 1 per ora, cercando di non assegnare troppe responsabilità al Modello di Dominio. Coloro che affrontano una situazione simile possono anche voler controllare le domande correlate Convalida e autorizzazione in architettura a strati e Validazione dell'input dei dati - Dove? Quanto? .

    
posta Lauri Harpf 07.09.2013 - 21:23
fonte

2 risposte

4

Quanto sono complesse le convalide? Spesso le convalide richiedono una combinazione di campi e / o regole aziendali che si basano sui campi per essere valutate con precisione.

Più complessa è la convalida, l'opzione più difficile e meno performante # 2 è.

Ovviamente il livello dati potrebbe invocare il servizio di convalida al momento della persistenza. Ciò potrebbe aiutare la strana situazione in cui i dati sono in uno stato non valido a causa di una modifica delle regole.

L'altro elemento che vale la pena di commentare è la possibilità di modificare le regole di convalida senza un ciclo qa di qualche tipo. Ma questo è un argomento per un thread diverso.

Considerate le informazioni di cui sopra, l'opzione 1 sembra la più flessibile se si assume che si mantenga la disciplina, separando la convalida e la persistenza.

    
risposta data 07.09.2013 - 21:44
fonte
0

Forse ti manca un livello. Senza conoscere i dettagli della tua applicazione (requisiti, architettura, ecc.) Farei qualcosa di simile Cliente (chiunque esso sia) - > Servizio applicazioni - > Modello di dominio

Il livello del servizio applicativo può interagire con il repository e il modello di dominio contenente la logica aziendale. Quindi puoi avere qualcosa come:

FieldUpdateService >> updateField(fieldId, newValue)
  List<FieldRule> rules = this.rulesRepository.findRulesFor(fieldId)
  Field field = this.fieldRepository.find(fieldId)
  try 
    field.updateValue(rules,newValue)
    fieldRepository.save(field)
  catch 
    // manage error

Se hai una relazione tra un Field e le sue FieldRules e usi un ORM come Hibernate in Java, farai solo:

FieldUpdateService >> updateField(fieldId, newValue)
  Field field = this.fieldRepository.find(fieldId)
  try 
    field.updateValue(newValue)
    fieldRepository.save(field)
  catch 
    // manage error

Field >> updateValue(newValue)
  for rule in rules
     if !rule.isValid(newValue)
        throw ValueNotAllowedException
  this.value = newvalue

Perché la query ORM crea un'istanza del grafico degli oggetti che chiedi.

Per quanto riguarda il tuo dubbio sul fatto che qualcuno possa fare

thisField.setValue(thatField.getValue())" and the Validation Rule of thisField might be violated without anyone being the wiser

Qualcuno potrebbe anche scrivere: FieldRule alwaysReturnTrueRule = new FieldRule {    isValid (newValue) {      ritorna vero;    } } field.updateValue ( "uncheckedValue", alwaysReturnTrueRule) È un esempio oscuro, ma quello che im traying to say è che nulla ti protegge da un cattivo uso del codice. Forse buona documentazione e comunicazione faccia a faccia. Penso che nulla ti protegga da un cattivo uso del codice.

    
risposta data 16.07.2015 - 21:57
fonte