Come rappresentare le relazioni tra più oggetti senza radice aggregata ovvia

5

Diciamo che abbiamo tre tipi di oggetti:

  • Agenzia
  • Chiamate
  • CallsQueues

Tutti possono essere collegati tra loro (1to1, 1toM, MtoM) o non hanno collegamenti e questi collegamenti cambiano nel tempo. Le chiamate attendono in CallsQueues per un agente gratuito, ogni agente offre una chiamata alla volta e gli agenti attendono le chiamate a zero, uno o più CallQueue contemporaneamente.

Da diverse prospettive nell'applicazione, ogni tipo può essere il tipo di root. Ad esempio, AgentMonitoringController fornisce un elenco di agenti con le loro chiamate correnti e CallQueues in cui si trova l'agente e QueuesMonitoringController fa lo stesso con le code come oggetti root.

Quali opzioni esistono per rappresentare i collegamenti tra gli oggetti? Le opzioni che ho in mente:

  1. Rendi ogni oggetto digita una classe. Inoltre, creare classi AgentToCallLink, CallToCallQueueLink e AgentToCallQueueLink che manterrebbero riferimenti a istanze concrete delle classi, come nelle relazioni molti-a-molti in un database relazionale. Memorizza elenchi di riferimenti alle classi di collegamento in entrambe le classi di entità.
  2. Come in 1, ma invece di creare classi di collegamento, memorizza solo elenchi di riferimenti a istanze concrete (che elimina la possibilità di inserire informazioni aggiuntive in collegamenti, come la creazione di datetime).

ORM e qualsiasi altro tipo di persistenza non è considerato.

Inoltre, da quale "collezione" dovrebbero essere prelevati questi oggetti per la manipolazione? Il repository per tipo è una buona idea (ad esempio AgentsRepository , CallsRepository , CallsQueuesRepository )?

    
posta G. Kashtanov 31.05.2018 - 20:27
fonte

2 risposte

5

IMHO dovresti iniziare prendendo il modello per primo. Non mi aspetto che un Call si verifichi in più di un CallQueue alla volta, quindi questa è solo una relazione 1:M . Inoltre, poiché un Agent può eseguire solo una chiamata alla volta (come hai scritto), questa è una relazione 1:1 . L'unica relazione M:M che vedo è quella tra agenti e code.

La tua domanda principale, tuttavia, rimane la stessa (tranne nella tua opzione 1 c'è solo una classe di collegamento richiesta, non tre). La risposta a ciò si riduce a quale livello di ignoranza della persistenza vuoi o devi raggiungere.

Per un modello completamente persistenza-ignorante, puoi provare a modellare un Call senza alcuna dipendenza in un'entità CallQueue e senza alcuna dipendenza in un'entità Agent . Questa è principalmente la tua opzione 2. Ad esempio, le code faranno riferimento alle loro chiamate da alcuni elenchi interni, gli agenti avranno un riferimento alla chiamata che attualmente servono, e gli agenti e le code possono fare riferimento l'un l'altro per liste. Naturalmente, se sono necessari attributi aggiuntivi per il collegamento tra agenti e code, sarà richiesta una classe come AgentToCallQueueLink .

Un vantaggio consiste nell'evitare le dipendenze cicliche (ad esempio, tra Call e CallQueues), il che rende le cose come test delle unità un po 'più semplici.

Tuttavia, l'ignoranza della persistenza non è gratuita:

  • raggiungere la persistenza è un po 'più difficile

  • lavorare con sottoinsiemi isolati dei dati (unità di lavoro) è più difficile, poiché ciò presuppone implicitamente che c'è una memoria persistente in background che tiene i dati al di fuori dell'unità di lavoro corrente

  • ottenere le transazioni corrette in un ambiente concorrente è un po 'più difficile (specialmente se non vuoi aggiungere CQRS al tuo sistema)

Ad esempio, modellando un oggetto Call con una chiave primaria CallID e un attributo chiave esterna CallQueueID , sapendo che verrà memorizzato in quel modo in un database, diventerà un po 'più facile alcuni chiamano oggetti di una coda dal database, manipolano la coda in isolamento e aggiornano il contenuto originale CallQueue senza la necessità di cancellare e riscrivere tutte le chiamate della coda. Tuttavia, ora hai introdotto una dipendenza ciclica tra CallQueues e Call s nel tuo modello di dominio (che può essere perfettamente accettabile, dal momento che in realtà è solo una debole dipendenza per ID, non di più).

In passato, ho spesso utilizzato entrambi: classi con tutti gli ID di chiavi esterne come attributi in una corrispondenza 1 a 1 alle colonne nel database e inoltre elenchi di riferimenti, che consentono di scrivere codice più conciso. Si tratta di informazioni ridondanti, ovviamente, ma questa ridondanza va bene fintanto che le chiavi sono immutabili e la modifica di un riferimento a un'altra istanza è vietata o correttamente incapsulata.

Quindi alla fine, è un trade-off. Se hai bisogno di persistenza e non hai bisogno di ignoranza della persistenza, vai con l'opzione 1. Un repository per tipo sarà ok. La mappatura dal modello di dominio a un database sarà più semplice. E se si considera di utilizzare un ORM, questo sarà un adattamento naturale (se modellate le classi direttamente in un modo che si adattano alle restrizioni che l'ORM impone su di esse).

Tuttavia, se pensi di aver davvero bisogno dell'ignoranza di persistenza, vai con l'opzione 2, ma non stupirti se persisterà, trattando le unità di lavoro che si sovrappongono e la gestione delle transazioni diventerà più difficile. L'opzione 2 è spesso più adatta per i modelli in cui non è richiesta alcuna persistenza, o l'unico modo per persistenza richiesto è caricare e salvare tutti i dati di un aggregato contemporaneamente, senza scrittori concorrenti. Per questo utilizzo, un repository per il caricamento e il salvataggio di tutto potrebbe essere ok.

    
risposta data 01.06.2018 - 10:54
fonte
1

Quasi sempre vado per il modello persistenza ignorante (opzione 2), a causa del principio di separazione delle preoccupazioni , ma dipende dall'implementazione molte volte . Ad esempio, se si utilizza un determinato Persistence Framework che, per impostazione predefinita, viene fornito con il proprio pattern MVC e in cui i modelli sono anche query builder (tipiche entità sensibili alla persistenza), allora ha senso opzione 1. MA non esattamente come lo stai presentando, con una classe per il link ogni , che non avrebbe senso.

Inoltre, l'opzione 2 non è una ricetta come fare sempre così , non è possibile farlo se è necessario memorizzare alcuni attributi nella relazione. MA ciò non implica che non puoi mescolare i due in un modello ignorante di persistenza, creando classi intermedie solo quando è necessario farlo.

Infine, farei quanto segue (ma è solo una scelta personale):

  • Chiama conosce l' Agente con un riferimento alla sua istanza e conosce anche il suo CallQueue corrente

  • CallQueue non conosce Call né contiene alcun elenco di riferimenti di alcun tipo. Penso che potrebbe portare a un codice disordinato. Se si desidera conoscere, ad esempio, l'elenco di tutte le chiamate appartenenti a una determinata coda, si otterrà tale risultato in PersistenceFacade, interrogando il proprio DB per tutte le chiamate che puntano a CallQueue .

  • A rigor di termini, l'agente non ha bisogno di conoscere alcuna istanza CallQueue . L'istanza Chiama conosce sia l'Agent che CallQueue, quindi tecnicamente sarai in grado di navigare quella relazione. Ciò complicherà le cose quando si desidera sapere, ad esempio, in quali code è un determinato agente. Altre domande da fare, compilazione di un elenco, ecc. Tieni presente che si tratta di una relazione N a N , quindi non puoi effettivamente avere un singolo riferimento CallQueue sull'agente, puoi utilizzare un elenco di riferimenti o, ancora meglio, utilizzarlo una classe intermedia CallQueueAgent solo per risparmiare tempo e semplificare le query. MA SOLO in questo caso, perché è un N a N , non negli altri.

  • La preoccupazione di trasformare un registro DB nel modello appropriato appartiene alla tua classe PersistenceFacade , che detiene anche la responsabilità di interrogare il DB. I moderni framework sono tutti dotati di una DB Facade che può perfettamente fare il lavoro. Essendo una facciata, hai tutto il freedoom per modificare la tua logica di persistenza come desideri, e non influenzerà affatto la tua logica di business o la tua modellazione.

È tutta la forza del pattern PersistenceFacade ed è per questo che ti consiglio di usarlo, applicando una modellazione non persistente consapevole per la tua modellazione di bussiness e il principio di separazione delle preoccupazioni .

    
risposta data 01.06.2018 - 22:12
fonte

Leggi altre domande sui tag