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.