Quando dico "Revisional Data Model", intendo un modello di dati in cui le informazioni non vengono mai perse: le eliminazioni non distruggono mai alcuna riga e gli aggiornamenti causano sempre un inserimento da qualche altra parte per preservare lo stato precedente di una riga prima dell'aggiornamento.
Ne ho alcuni di cui ho letto e ascoltati dai colleghi:
- Trigger nel database. Il motivo principale per cui non mi piace questo approccio è perché se si dipendono dai trigger per applicare l'integrità della revisione, l'applicazione diventa legata al server del database. Se si utilizza un ORM con diversi provider (Oracle, MySQL, T-SQL, ecc.), Potrebbe essere necessario modificare alcuni SQL per passare da uno all'altro.
- Registri di traccia leggibili dall'uomo L'anno scorso ho giocato a Business Analyst per un progetto con un nuovo sviluppatore perché uno dei nostri dipartimenti affiliati era stato sbattuto con scadenze importanti. Ha insistito sul fatto che, nonostante i requisiti che ho raccolto e gli schemi che ho modellato, avrebbe inserito una procedura memorizzata da eseguire con ogni istruzione di creazione / aggiornamento / cancellazione. Questo sproc dovrebbe semplicemente scrivere i vecchi dati come una stringa formattata e salvarlo in una tabella ERRORS_LOG nel database. Il suo obiettivo era di aiutarla a eseguire il debug dell'applicazione nel caso qualcosa fosse andato storto: voleva una traccia.
- Creazione di tabelle sorelle. Questo approccio è come avere 2 database che vivono nello stesso contesto. Per la tabella Person ci sarebbe una tabella PersonHistory, per Product ci sarebbe ProductHistory e così via. Gli schemi sarebbero identici e ogni volta che si verificava una modifica nella tabella delle entità, lo stato precedente sarebbe stato inserito in ProductHistory.
Se si dispone di un'applicazione in cui si desidera consentire agli utenti di "ripristinare" o "ripristinare la versione precedente" di un'entità, solo # 3 sembra una soluzione praticabile. Tuttavia, poiché ogni coppia (entità e relativa cronologia) ha lo stesso modello / schema, possono essere combinati. Aggiungendo proprietà bool / bit come IsCurrentVersion e IsDeleted, è possibile suddividere un singolo set di entità in stati diversi all'interno della clausola WHERE. Considera questo esempio # 4.
Uno svantaggio del # 4 è che le tabelle del database diventano difficili da leggere quando si selezionano tutte le righe. I dati sull'estrazione da un ide server DB richiederebbero un uso piuttosto esteso delle viste, che causa lo stesso problema dell'opzione n. 1. Guardare tutti i diversi stati insieme nella stessa tabella rende molto più difficile leggere. Con l'approccio n. 3, devi cercare in una tabella diversa per trovare versioni precedenti e amp; cancella.
Abbiamo utilizzato l'approccio n. 4 con successo. Abbiamo un supertipo di RevisableEntity con proprietà che indicano quando i dati sono stati modificati, chi l'ha modificato, se è stato cancellato, archiviato o modificato. Gli aggiornamenti si verificano sulle righe proprio come farebbero in circostanze normali. Tuttavia, come parte della stessa azione, viene inserito un inserto corrispondente nella stessa tabella / set di entità. L'inserto contiene gli stessi valori dell'aggiornamento prima che fosse aggiornato. La riga aggiornata, oltre a tutte le modifiche apportate dall'utente, aggiorna anche le proprietà when e who.
Ora per la mia domanda: A chi appartiene questo codice? nel dominio o nell'implementazione del repository? Ci sono alcuni aspetti che dipendono dallo storage, il che suggerisce di inserire il codice in cui si trova l'accoppiamento ORM. Tuttavia ci sono altri aspetti di cui i livelli dominio / applicazione dovrebbero essere in grado di fidarsi. Forse le entità di dominio dovrebbero gestire la propria integrità di revisione, o forse ci dovrebbe essere una fabbrica da coordinare?