Progettazione di database per oggetti con più stati

5

Contesto

Sto progettando un database che, semplificato, dovrebbe essere in grado di gestire gli utenti che inviano richieste di lavoro a vicenda e, successivamente, un lavoro può essere avviato, completato e rivisto. Il design dovrebbe essere scalabile (pensa a milioni di utenti).

Approcci che ho considerato:

Tavola gigantesca

Un approccio, probabilmente non il migliore, sarebbe semplicemente archiviare TUTTI i lavori in una tabella enorme jobs . Questa tabella richiede una colonna state per rappresentare lo stato in cui si trova attualmente il lavoro (ad esempio ACCEPTED , STARTED , FINISHED , REVIEWED e.t.c.). Il problema più grande con questo approccio che posso vedere è che i lavori in diversi stati hanno diversi tipi di dati che sono rilevanti per loro. Ad esempio, una richiesta di lavoro ha un prezzo concordato preliminare, ma potrebbe cambiare prima che il lavoro venga avviato e cambiare nuovamente prima che il lavoro sia terminato. Questo potrebbe naturalmente essere risolto aggiungendo semplicemente più colonne alla tabella e assegnandole un nome appropriato, ma probabilmente diventerà un enorme collo di bottiglia, molto presto per avere una tabella contenente tutti i diversi tipi di dati possibili per tutti i diversi stati possibili di un lavoro.

Tabelle diverse per stati diversi

Questo approccio sarebbe avere più tabelle, ad esempio job_requests , jobs_started , jobs_finished , tabelle che a loro volta possono avere sottostati, ad es. job_requests potrebbe avere i sotto-stati PENDING , ACCEPTED , mentre la tabella jobs_finished avrebbe i sottoambieri COMPLETED , CANCELLED , REVIEWED .

Con questo approccio ogni tabella contiene solo dati rilevanti per lo stato corrente del lavoro, ma d'altra parte alcuni dati potrebbero essere duplicati (ad esempio gli ID utente del richiedente lavoro e il destinatario del lavoro - d'altro canto questa informazione potrebbe essere memorizzata in un'altra tabella?). Il problema con questo approccio è che non riesco a pensare a una buona soluzione su come archiviare tutte le informazioni durante la transizione tra stati. Ad esempio, una volta che una richiesta di lavoro è stata accettata e quindi avviata, deve essere eliminata dalla tabella job_requests e spostata nella tabella jobs_started , ma è solo una questione di tempo prima che uno stakeholder voglia sapere, ad esempio, come a lungo il tempo medio è tra una richiesta di lavoro che viene creata, fino a quando non è stata avviata, a quel punto avrei bisogno dei dati dalla tabella job_requests per essere in grado di calcolarlo.

Sembra che questo tipo di problema debba essere facile da risolvere, ma non riesco davvero a pensare a nessuna buona soluzione che "si senta a posto", qualsiasi soluzione mi venga in mente mi sembra brutta e posso immediatamente pensare a un certo numero di cose che rendono la soluzione cattiva.

Molto grato per qualsiasi feedback o consiglio sugli approcci che potrei prendere. Grazie in anticipo!

    
posta Atra Azami 07.01.2015 - 19:26
fonte

4 risposte

9

Sembra che tu abbia 3 categorie principali di dati che stai tentando di memorizzare:

  1. Dati di lavoro generali (id di lavoro, id del richiedente, id del destinatario del lavoro ecc.)
  2. Transizioni di stato (lavoro avviato, lavoro terminato)
  3. Dati di lavoro specifici dello stato
  4. (facoltativo) eventi relativi al lavoro (prezzo modificato, utente ricevente di lavoro riassegnato ecc.)

La chiave è separare i dati simili ad eventi da qualsiasi altra cosa.

Schema design

Ecco alcuni dettagli:

1. Tabella dei lavori generali

Tutte le informazioni che NON sono specifiche dello stato entrano nella tabella dei "lavori" (diciamo). Chiave primaria generata automaticamente: job_id

2. Tabella delle transizioni di stato

Tutte le informazioni sulle transizioni di stato vanno nella tabella "job_state_transitions", che potrebbe avere le seguenti colonne:

  • job_transition_id
  • job_id
  • created_at
  • from_state
  • to_state

Idealmente questa tabella è solo append. Niente è aggiornato o rimosso qui.

Usando questa tabella, puoi scoprire lo stato più recente di un particolare lavoro selezionando la riga più recente per un dato job_id dalla tabella job_transitions. Puoi ulteriormente denormalizzare questo e introdurre una colonna "job_state", i cui contenuti vengono aggiornati ogni volta che una nuova riga viene inserita nella tabella job_transition (le stored procedure potrebbero aiutarti qui se questo è il tuo problema).

Puoi anche fare ogni sorta di analisi sulle transizioni di stato, perché i dati di temporizzazione vengono mantenuti (create_at è un campo data / ora che può aiutarti)

3. Dati di lavoro specifici dello stato

Tutti i dati specifici dello stato vanno nelle tabelle "[state] -jobs". Chiave primaria: un identificativo di sequenza. Indice principale: job_transition_id

4. Dati degli eventi facoltativi

È inoltre possibile introdurre una tabella "audit trail" che consente di tenere traccia delle varie modifiche che gli utenti potrebbero richiedere per ciascun lavoro, ad esempio il "cambio concordato in base al prezzo". Questa è una generalizzazione della tabella di transizione dello stato: una tabella principale che contiene eventi e una tabella supplementare per ogni tipo di evento (ad esempio, tabella price_changes con job_id, created_at, from_price e to_price columns).

Ridimensionamento a milioni di utenti

Se la tabella "lavori" principale diventa troppo complessa, puoi dividerla con job_id o requesting_user_id o qualcosa del genere.

Allo stesso modo, la tabella degli eventi dovrebbe essere solo append e può essere ruotata o eliminata dagli eventi relativi ai lavori che sono stati completati.

    
risposta data 07.01.2015 - 20:33
fonte
4

Che ne dici di un ibrido tra i due? Una grande tabella con un JobID e uno stato (e forse altre informazioni comuni a tutti i lavori e gli stati), con altre tabelle per gestire le informazioni aggiuntive sullo stato. Ciò riduce (o elimina) la duplicazione dei dati, ma mantiene le cose più gestibili.

    
risposta data 07.01.2015 - 20:24
fonte
1

Potresti prendere in considerazione l'utilizzo di un database noSQL per questi dati specifici. In questo modo puoi semplicemente inserire object in un record, indipendentemente dalle "colonne".

A seconda del sistema di database scelto (e della soluzione implementata), se si desidera è possibile sovrascrivere un record con dati diversi. Naturalmente, puoi anche salvare i tuoi dati storici e solo PUT un nuovo record nel database.

Puoi iniziare con questo record:

myStartedObject{
    id: 1,
    state:  'started',
    agreedUponPrice:    100.00,
    someOtherData:  'x'
}

e nel tempo cambierebbe in qualcosa del genere, o se si desidera conservare i record della cronologia, la id cambierebbe.:

myFinishedObject{
    id: 1
    state:  'finished',
    finishedPrice:  209.00,
    someFinishedData:   'y'
}

Memorizzare tutte le date dello stato all'interno dell'oggetto è anche un'opzione, naturalmente, qualcosa del tipo:

myObject
{
    id: 1,
    states: [{ state: 'started', agreedUponPrice: 100.00, someOtherData: 'x'}, 
        { state: 'finished', finishedPrice: 209.00, someFinishedData: 'y'}]
}

Tenete a mente, questo sicuramente non è il punto d'argento, ma potrebbe essere qualcosa da considerare quando affermate che lo 'schema' è piuttosto lento e non volete inquinare il vostro RDBMS con tutte quelle colonne.

Ricorda anche che qualsiasi RDBMS può fare anche questo. So che in MS SQL hai il tipo di dati XML. Con questo, è possibile memorizzare i dati in questa colonna XML. Questa colonna XML può contenere il tuo oggetto deserializzato, per esempio. Non è una soluzione noSQL 'reale', ma sembra un po 'come quella.

    
risposta data 09.01.2015 - 14:05
fonte
-1

Se consideri questo problema come un problema di flusso di lavoro, puoi utilizzare uno degli strumenti del flusso di lavoro come jBPM o Windows Workflow Foundation e concentrarti sulla logica di business e lasciare che lo strumento gestisca la persistenza per te.

    
risposta data 08.01.2015 - 03:20
fonte