Utilizzare un oggetto valore personalizzato o un Guid come identificatore di entità in un sistema distribuito?

4

tl; dr

Mi è stato detto che nella progettazione basata sul dominio, un identificatore per un'entità potrebbe essere un oggetto valore personalizzato, cioè qualcosa diverso da Guid , string , int , ecc. Può essere davvero consigliabile in un sistema distribuito?

Versione lunga

Inventerò una situazione analoga a quella che sto attualmente affrontando.

Dire che ho un sistema distribuito in cui un concetto centrale è un uovo. Il sistema consente di ordinare uova e visualizzare report di spesa e dati incentrati sull'inventario, come quantità a portata di mano, utilizzo, valutazione e ciò che hai. C'è una varietà di servizi che supportano questi comportamenti. E diciamo che c'è anche un'altra app che ti permette di comporre ricette che si collegano a un particolare tipo di uovo.

Ora il tipo di uovo è suddiviso tra specie di struzzo, oca, anatra, pollo, quaglia. Questo è bello e dandy perché significa che gli utenti non finiscono con le uova di struzzo quando vogliono uova di quaglia e quant'altro. Tuttavia, abbiamo ricevuto lamentele perché le uova di pollo jumbo non sono nemmeno simili a quelle piccole. Il prezzo è diverso, e in realtà non sono sostituibili nelle ricette. E qui pensavamo di fare un favore agli utenti non sovraccaricandoli con troppe opzioni.

Attualmente ciascuno dei servizi (diciamo OrderSubmitter , EggTypeDefiner , SpendingReportsGenerator , InventoryTracker , RecipeCreator , RecipeTracker , o qualsiasi altra cosa) identificano i tipi di uova con una rappresentazione integer standard di settore specie (chiamiamola speciesCode ). Ci rendiamo conto di aver rimediato perché questa modifica potrebbe influire su ogni servizio.

Ci sono due soluzioni di base proposte:

  1. Utilizza un tipo di identificatore predefinito come Guid come eggTypeID in tutti i servizi, ma rendi EggTypeDefiner l'unico servizio che sa che esegue il mapping a speciesCode e eggSizeCode (e potenzialmente a isOrganic flag in futuro, o qualsiasi altra cosa).
  2. Utilizza un oggetto valore EggTypeID che è una combinazione di speciesCode e eggSizeCode in ogni servizio.

Ho proposto la prima soluzione perché spero che incapsuli meglio la definizione di che tipo di uovo è nella EggTypeDefiner e sarà più resistente ai cambiamenti, diciamo se alcune persone ora vogliono differenziare le uova oppure no sono "organici".

La seconda soluzione è stata suggerita da alcune persone che capiscono il DDD meglio di me, nella speranza che meno arricchimento e ricerca saranno necessari in questo modo, con la giustificazione che in DDD si utilizza un oggetto valore come ID. Inoltre, stanno dicendo che EggTypeDefiner non è un dominio e EggType non è un'entità e come tale non dovrebbe avere Guid per un ID.

Tuttavia, non sono sicuro che la seconda soluzione sia valida. Questo "oggetto valore" dovrà essere serializzato in JSON e URL per le richieste GET e utilizzato con una varietà di tecnologie (C #, JavaScript ...) che interrompe l'incapsulamento e quindi rimuove qualsiasi comportamento dell'oggetto identificativo (è o dei campi facoltativi? ecc.) È un caso in cui vogliamo evitare qualcosa che normalmente andrebbe bene in DDD perché stiamo provando a fare DDD in modo distribuito?

Sommario

Può essere una buona idea usare un oggetto valore personalizzato come identificatore in un sistema distribuito (soluzione # 2)?

    
posta Kazark 15.05.2014 - 22:45
fonte

1 risposta

1

Se dovessi semplificare un po 'i requisiti di base, sembra che il problema sia il seguente:

  1. Disegna il tipo di uovo in modo che possa essere esteso in futuro per contenere attributi aggiuntivi
  2. Se tali estensioni possono essere retrocompatibili o meno (e se debbano essere retrocompatibili o meno)

Penserei che il problema di compatibilità con le versioni precedenti sarà il vero problema da risolvere. Esaminiamo l'esempio che hai fornito:

Currently each of the services (say, OrderSubmitter, EggTypeDefiner, SpendingReportsGenerator, InventoryTracker, RecipeCreator, RecipeTracker, or whatever) are identifying egg types with an industry-standard integer representation the species (let's call it speciesCode). We realize we've goofed up because this change could effect every service.

Allontanandoci dall'implementazione per un secondo, diciamo che il tipo di uovo ora include un codice di specie e un codice di dimensione. Ora diciamo che è necessario aggiungere un nuovo attributo supponendo che, indipendentemente dal numero di attributi aggiunti, è possibile aggiungerne di nuovi in futuro. Come dovrebbero comportarsi i servizi esistenti che già fanno riferimento a tipi di uova esistenti?

Un approccio, che potrebbe essere abbastanza accettabile in questo caso, potrebbe essere che ogni nuovo attributo è un attributo 'opzionale', il che significa che c'è un default o un flag "non importa" e che i servizi esistenti continuano a operare in base all'insieme di attributi esistente. Questo problema è più facile da risolvere se ipotizziamo che gli attributi verranno aggiunti (il tuo caso e mai modificati o rimossi, il che è un problema di versioning più complesso).

Se questo approccio è accettabile, progetterei un tipo di oggetto (tipicamente una classe) che contenga queste varie proprietà e assicuri che la serializzazione e la deserializzazione in vari formati sia compatibile con le versioni precedenti.

Ad esempio, assumiamo quanto segue (assumiamo lo pseudocodice):

class EggType
{
    string speciesCode;
    string sizeCode;
}

string SerializeForURL()
{
    return speciesCode + "." + sizeCode;
}

Ora, diciamo che abbiamo aggiunto una nuova proprietà, chiamata colorCode. Ma poiché questo è facoltativo, assumiamo che un tipo di uovo possa essere creato senza fornire un colorCode. In questo caso, il codice dovrebbe gestire il caso in cui viene fornito colorCode rispetto a dove non è fornito. Diciamo che se uno dei servizi che utilizza questo tipo di uova inserisce ordini per nuove uova dalle fattorie, tale servizio dovrà essere modificato per supportarlo, altrimenti le nuove uova saranno colori casuali indipendentemente da ciò che è stato ordinato. Tuttavia, diciamo che esiste un servizio che raccoglie i dati di vendita su ciò che viene venduto, può semplicemente non aggregarsi per colorCode (se il codice non è ancora aggiornato) e non riportarlo nella catena di gestione, fino a quando non viene apportata tale modifica. Questa decisione su quando sostenere può quindi riposare sui rispettivi servizi.

Potremmo quindi modificare serializeForUrl () in modo simile a questo:

string SerializeForURL()
{
    return speciesCode + "." + sizeCode + "." + colorCode;
}

Nel metodo di deserializzazione, potremmo verificare se ci sono due attributi o tre. Se due, colorCode non viene fornito, ma se tre, viene fornito colorCode.

È più semplice nel caso di JSON, perché il valore per colorCode sarebbe nullo.

Preferirei questo approccio sui GUID in qualsiasi giorno, per diversi motivi:

  • Rende più difficile gestire il cambiamento nel tipo di uovo: non consente l'isolamento di questi attributi gli uni dagli altri, anche se sono realmente indipendenti l'uno dall'altro. (Ad esempio, se modifichi il GUID, tutti i servizi devono capirlo, anche se tutto ciò che potrebbe fare è aggiungere una proprietà, come colorCode, a cui molti servizi potrebbero non interessare affatto.)
  • Tutti i sistemi ora dipendono da un elenco globale di GUID e da come si traducono in un tipo di uovo - accoppiamento non necessario secondo me.
  • Più facile da eseguire il debug: i programmatori sono uno degli utenti del codice e devono essere in grado di leggere facilmente il codice così come i registri di traccia, i dati di controllo, qualunque sia.
risposta data 11.06.2014 - 08:52
fonte