Gli ID devono essere associati al tipo di implementazione

2

Questa è una questione di design del modello, e spero che alcuni nerd di architettura abbiano opinioni forti su questo. Ne ho sviluppato uno che pubblicherò in una risposta.

Tipicamente, in tutti i codebase che ho mai visto, se il tuo modello ha un ID intero, è digitato come int e se ha un id guid è un Guid . E 'davvero corretto? È questa astrazione che perde? E 'un'astrazione che vale la pena mantenere?

    
posta George Mauer 19.01.2015 - 06:25
fonte

2 risposte

4

L'uso di tipi nativi ( int , Guid , ecc.) invece di tipi che esprimono l'intento ( Price , ProductIdentifier , ecc.) ha due svantaggi :

  1. La modifica del tipo è sovrapposta a tutto il codice base , il che la rende particolarmente dolorosa.

    Ad esempio, passare da smallint ( short ) a uniqueidentifier ( Guid ) non è solo doloroso a livello di database (come si trasformano i dati di produzione da campi auto-incrementati a UID generati casualmente?) , ma anche a livello di codice. Devi passare attraverso l'intero codice base e sostituire short di Guid , eventualmente trattare con errori di compilazione o persino errori di runtime (in un caso di linguaggio dinamico o quando viene usato Reflection).

  2. I metodi relativi al tipo sono diffusi attraverso il codice base invece di essere vincolati all'interno di una singola classe. Questo crea un rischio di duplicazione del codice .

    Quando hai ProductIdentifier , puoi facilmente individuare il metodo CheckIfValueInRange : ovviamente, è in ProductIdentifier class e solo lì.

    Quando hai int come identificatore del prodotto, puoi indovinare che CheckIfValueInRange è nel livello di accesso ai dati, ma poi spetta a te individuarlo. Sotto pressione, alcuni programmatori potrebbero essere riluttanti a passare il tempo a cercare il metodo e scrivere il proprio correttore:

    if (this.Id <= 0 || this.Id > 100000)
    {
        throw new Exception("The product ID is invalid.");
    }
    

    che potrebbe sfortunatamente differire da un'altra implementazione che è più conforme alle specifiche:

    const int MAX_ID = 100000;
    if (this.Id < 0 || this.Id > MAX_ID)
    {
        throw new OutsideRangeException("The identifier is outside the allowed range.");
    }
    

    che porta a un incubo di refactoring / manutenzione, che potrebbe essere facilmente evitato se si stesse utilizzando la classe di ProductId .

Gli identificatori cambiano spesso il loro tipo?

In realtà, lo sono. Uno sviluppatore decide di utilizzare smallint per un sito Web di piccole dimensioni; il sito Web diventa estremamente popolare e una volta raggiunto il limite di 32.767 valori, il tipo deve essere modificato. Un altro sviluppatore utilizza uniqueidentifier , e successivamente sembra che non ci siano vantaggi per questo tipo nel contesto reale, ma avere un valore auto-incrementato, ordinato sarebbe bello.

Ciò rende gli identificatori un buon candidato per l'utilizzo di un tipo personalizzato anziché nativo.

Gli identificatori hanno una logica aziendale?

A parte il controllo dell'intervallo (vedi sopra), ci sono molti casi in cui ha senso collegare la logica aziendale all'identificatore stesso:

  • Generazione di un nuovo ID,

  • Verifica dell'esistenza di un ID,

  • Controllare se l'ID appartiene a un gruppo specifico (come l'ID da 1 a 10000 è il gruppo A e gli ID superiori a 10000 sono il gruppo B),

  • ecc.

Si noti che rimuovendo l'astrazione che perde l'ORM attraverso l'uso di un tipo personalizzato, si crea anche una nuova astrazione che perde. Immagina di voler ordinare qualcosa per ID. Se ProductId corrisponde a un campo auto-incrementato, ha senso. Se ProductId corrisponde a un GUID, l'ordinamento non ha senso e dovresti cercare un criterio diverso, come Product.CreatedTimeUtc .

    
risposta data 19.01.2015 - 07:37
fonte
3

Quindi ultimamente ho pensato che semanticamente l'id è un identificatore univoco; il fatto che sia presente nel numero intero o nel formato guid è un dettaglio di implementazione e l'esposizione del fatto nel tipo è un'astrazione che perde.

Pensandoci su, esponendola come una stringa o un tipo Identifier dedicato ha alcuni vantaggi, rende le cose più facili da prendere in giro, ripetere o passare agli identificatori ordinati. Ovviamente, il database dovrebbe essere ciò che lo rende preformante, ma questo è un altro dettaglio di implementazione.

La mia intuizione è che un tipo Identifier dedicato è la strada da percorrere, e se il tuo ORM non lo supporta, una stringa sembra una seconda opzione migliore.

    
risposta data 19.01.2015 - 06:27
fonte