IMHO você deve começar por acertar o modelo primeiro. Eu não esperaria que um Call
ocorresse em mais de um CallQueue
de cada vez, portanto, essa é apenas uma relação 1:M
. Além disso, como um Agent
pode apenas chamar um servidor de cada vez (como você escreveu), esse é um relacionamento 1:1
. A única relação M:M
que vejo é aquela entre agentes e filas.
Sua questão principal, no entanto, permanece a mesma (exceto em sua opção 1, há apenas uma classe de link necessária, não três). A resposta para isso se resume a qual nível de ignorância de persistência você quer ou precisa alcançar.
Para um modelo totalmente ignorante de persistência, você pode tentar modelar um Call
sem nenhuma dependência para uma entidade CallQueue
e sem nenhuma dependência para uma entidade Agent
. Esta é principalmente sua opção 2. Por exemplo, as filas farão referência a suas chamadas por algumas listas internas, os Agentes terão uma referência à chamada que eles atendem no momento, e os agentes e filas podem referenciar-se uns aos outros por listas também. Obviamente, se você precisar de atributos adicionais para o link entre agentes e filas, será necessária uma classe como AgentToCallQueueLink
.
Uma vantagem disso é evitar dependências cíclicas (por exemplo, entre Chamadas e CallQueues), o que torna as coisas como testes de unidade um pouco mais fáceis.
No entanto, a ignorância da persistência não vem de graça:
-
alcançar a persistência é um pouco mais difícil
-
trabalhar com subconjuntos isolados dos dados (unidades de trabalho) é mais difícil, pois isso pressupõe implicitamente que há um armazenamento persistente no plano de fundo mantendo os dados fora da unidade de trabalho atual
-
obter as transações corretas em um ambiente concorrente é um pouco mais difícil (especialmente se você não quiser adicionar o CQRS ao seu sistema)
Por exemplo, modelando um objeto Call
com uma chave primária CallID
e um atributo de chave estrangeira CallQueueID
, sabendo que ele será armazenado dessa maneira em um banco de dados, será um pouco mais fácil extrair um alguns Chamar objetos de uma fila do banco de dados, manipular a fila isoladamente e atualizar o conteúdo original de CallQueue
sem a necessidade de excluir e reescrever todas as chamadas da fila. No entanto, agora você introduziu uma dependência cíclica entre CallQueues
e Call
s em seu modelo de domínio (o que pode ser perfeitamente aceitável, já que na verdade é apenas uma dependência fraca por IDs, não mais).
Então, no final, é um trade-off. Se você precisar de persistência e não precisar de ignorância de persistência, escolha a opção 1. Um repositório por tipo será ok. Seu mapeamento do modelo de domínio para um banco de dados será mais simples. E se você considerar usar um ORM, este será um ajuste natural (se você modelar suas classes diretamente de uma maneira que elas se ajustem às restrições que o ORM impõe a elas).
No entanto, se você acha que realmente precisa de ignorância persistente, escolha a opção 2, mas não fique surpreso se persistência, lidar com unidades de trabalho sobrepostas e manuseio transacional se tornarem mais difíceis. A opção 2 geralmente é mais adequada para modelos em que não é necessária persistência ou a única maneira de persistência necessária é carregar e salvar todos os dados de um agregado de uma só vez, sem gravadores simultâneos. Para esse uso, um repo para carregar e salvar tudo pode estar ok.