Come evitare di mescolare la chiave surrogata con il resto dei dati

5

Supponiamo di avere un CRUD molto semplice in cui stai semplicemente memorizzando un semplice tipo di dati simile a una tupla, ma la sua chiave primaria naturale è ingombrante da ricordare (molto lunga, difficile da scrivere, ecc.). Inoltre, supponiamo che voglio consentire agli utenti di fare riferimento ai record usando un handle come un valore intero autoincrementato (che fungerà quindi da chiave primaria, ma è surrogato, piuttosto che naturale ).

Creano i record senza specificare l'handle, ma, quando richiedono una lista di tutti i record, voglio dare loro i record con l'handle ad essi collegato, in modo che possano poi facilmente riferirsi a loro.

Se, nella mia applicazione, il record è rappresentato come un oggetto valore semplice, (che è il proprio DTO), che viene creato e passato al gateway del database e restituito dal gateway del database ... come potrei io correttamente fare anche il ritorno dell'impugnatura?

Non voglio restituire un oggetto diverso (qualcosa come un record decorato con l'handle) durante la lettura dal database, perché ritengo che l'handle sia solo un dettaglio di presentazione richiesto dai requisiti di questa specifica interfaccia utente e potrebbe essere inutile per diverse interfacce utente. Attaccare l'handle all'oggetto record esistente non ha senso perché non fa realmente parte del tipo di dati.

Una cosa che potrei fare è che la presentazione richieda separatamente l'handle di ogni record. Ciò significherebbe due viaggi nel database, ma almeno sarebbe chiaro che l'handle non fa parte del record.

Ma sono positivo, deve esserci un modo migliore. Qualche idea?

Ad esempio

Un catalogo di libri molto semplice. Il dato è Book, che è rappresentato da tuple del modulo (autore, titolo, anno). Supponiamo che (autore, titolo) sia una chiave primaria naturale (in tutto il rigore, potrebbe non essere il caso, ma prendi questo come presupposto del sistema). Ho solo un'interfaccia utente, che è un'interfaccia a riga di comando per il mio sistema. Qui, richiedere agli utenti di fare riferimento ai libri fornendo la chiave primaria naturale (autore, titolo) è ingombrante, quindi decido di collegare ogni record a un numero intero stupido autoincrementato che agirà come una chiave primaria surrogata. In questo modo, gli utenti dell'interfaccia della riga di comando possono fare riferimento ai record tramite il relativo handle di numero intero associato. Questo collegamento può essere implementato come una tabella di associazione sparate che mappa i PK naturali per la sostituzione dei PK.

L'intera implementazione del sistema consiste in un tipo di dati, che può essere un vecchio oggetto semplice tuple / struct / hash / custom e un singolo repository CRUD in cui posso mantenere questi oggetti e leggerli con il loro naturale chiave primaria. Non voglio restituire un oggetto che ha anche un membro 'handle' contenente la sua chiave primaria surrogata associata, poiché i dati non hanno nulla a che fare con il resto dei dati e sono solo un requisito dell'interfaccia utente particolare.

Quindi proporrei di avere un metodo sul repository (o un repository diverso del tutto) che mi fornisca le maniglie dei record, fornendo la chiave primaria naturale. Con questo, questa UI specifica userebbe il repository (o quei due repository) per recuperare prima il record e poi recuperare l'handle, in due passaggi.

    
posta Randy Eels 25.08.2016 - 19:20
fonte

3 risposte

7

Non vedo perché ti senti così.

"Sticking the handle to the existing record object makes no sense because it is not really part of the data type."

Se l'utente deve identificare un record con un numero arbitrario o incrementale, allora sono dati. Puoi anche dargli un nome generico come "Numero ID". Molti ordini d'acquisto (numero PO) e sistemi di fatturazione (ID fattura) hanno questi numeri. In alcuni punti, tenere traccia di questi numeri sequenziali è molto importante in una verifica. Non si può fare affidamento su questo per un aggiornamento o cancellazione (non è la chiave primaria), va bene. Non inviarlo di nuovo al db se non si desidera.

    
risposta data 25.08.2016 - 20:29
fonte
1

Ci sono stato, fatto.

Ho programmato app CLI che interagiscono con gli elementi del database in cui la chiave naturale è macchinosa da digitare.

Quello che ho fatto è che ho progettato la mia app in modo che il caso d'uso sia sempre il seguente:

  • L'utente digita il comando seguito da una stringa.
  • Quella stringa è qualcosa che l'utente ricorda dalla descrizione dell'elemento rappresentato nella riga del database (esempi: un film, una persona, un libro, ecc.)
  • L'app filtra il database con LIKE
  • Altri parametri e opzioni possono far restringere la query al numero di righe su cui il comando LIKE deve agire.
  • Se viene recuperato un numero gestibile (per un CLI), ad esempio 15 al massimo, viene presentato un menu. Il menu consiste nel stampare ogni riga (un gruppo selezionato di colonne) preceduta da un numero, ma quel numero non è il surrogato, è il numero di riga di un file temporaneo creato dopo la ricerca con i surrogati delle righe. L'utente selezionerà sempre da un menu e i numeri saranno sempre da 1 a 15 al massimo.
  • Se il numero di righe superiore a 15 stampo un messaggio in modo affermativo e che dovrebbero affinare la ricerca.
  • Si noti che qui, come nelle GUI, si offre all'utente un modo per scegliere un record senza conoscere il surrogato (nelle GUI l'utente fa clic sull'elemento o lo seleziona da un menu a discesa).

In un'altra app CLI, dovevo mostrare la surrogata in modo che gli utenti potessero usarla come parametro per il programma. In quel caso non ho usato l'approccio di menu perché l'app era uno strumento di manutenzione CLI per i manutentori del sistema, facendo una serie di operazioni molto complesse sui dati, e potevano e dovevano ottenere il surrogato per passarlo come parametro per il programma.

Bottom-line:

I don't want to return an object which also has a 'handle' member containing its associated surrogate primary key because that data has nothing to do with the rest of the data and is there just as a requirement of the particular UI.

Le chiavi surrogate non sono requisiti delle interfacce utente, vengono utilizzate perché la chiave naturale è inesistente, errata (cambia spesso) o ingombrante (troppe colonne)

Prova l'approccio del menu, se non ti soddisfa, (come nel secondo esempio che ti ho dato) quindi mostra la surrogata. Se la chiave naturale è così ingombrante da digitare, allora la surrogata fa parte dei dati.

    
risposta data 03.09.2016 - 17:42
fonte
1

I don't want to return an object which also has a 'handle' member containing its associated surrogate primary key because that data has nothing to do with the rest of the data and is there just as a requirement of the particular UI.

Il design del tuo sistema diventa molto più semplice se cambi idea su questo. Avere oggetti dati con una corrispondenza 1: 1 tra gli attributi dell'oggetto e le colonne del database è un approccio diretto che serve molto la maggior parte dei sistemi che ho visto in passato. Rende molto facile generare l'intero codice CRUD e il codice per gli oggetti dati stessi da una meta descrizione o dal database, invece di scriverlo manualmente.

E sì, questi oggetti potrebbero avere una dozzina di attributi, di cui una particolare forma, finestra o lista dell'interfaccia utente mostrerà solo sei, sette o dieci, e un altro modulo mostrerà o userà tre diversi. Il fatto che l'attributo che non vuoi mostrare in una forma è una chiave surrogata, o un qualche tipo di dati aziendali, o un altro elemento tecnico che non ha nulla a che fare con l'interfaccia utente, è irrilevante.

Naturalmente, ho anche visto sistemi in cui si possono omettere alcune colonne e consentire il recupero di oggetti "parziali", dove alcuni degli attributi non sono inizializzati dopo la prima creazione dell'oggetto e potrebbero essere caricati successivamente da una seconda query . AFAIK alcuni ORM forniscono alcuni meccanismi automatici di "caricamento lento" che funzionano esattamente in questo modo. Ma per la maggior parte delle situazioni, si tratta di un'ottimizzazione non necessaria, poiché le prestazioni delle query del database sono principalmente dominate dal numero di roundtrip di un SELECT. Spesso non fa alcuna differenza misurabile se selezioni 3, 6 o 12 colonne da una tabella, eccetto che una delle colonne rimaste è un campo BLOB o un campo di testo enorme.

Quindi fai un favore a te stesso e cerca di non sovraccaricare il tuo sistema adattando i tuoi oggetti dati a tutti i requisiti di una parte specifica dell'interfaccia utente. Inizia con un oggetto dati per tabella, contenente tutte le colonne e un gruppo di operazioni CRUD, anche se questo significa includere un attributo chiave surrogata di cui non hai bisogno ovunque. Se ti trovi davvero nei guai, ottimizza più tardi quando arrivi a quella situazione.

    
risposta data 03.09.2016 - 19:48
fonte

Leggi altre domande sui tag