Identificador vs objeto de domínio como um parâmetro de método

10

Existem argumentos objetivos a favor ou contra o uso de objetos versus ID exclusivo como parâmetros de método / função? (e membros de outros objetos?). Especialmente no contexto de linguagens de tipo estaticamente (C # / Java / Scala)

Prós do próprio objeto:

  • Mais chamadas seguras. Com os IDs, existe o risco de ordem incorreta dos argumentos. Isso pode ser atenuado, mantendo uma classe 'mini' para cada classe que armazena apenas o ID dessa classe.
  • Obtenha uma vez de persistência, não há necessidade de obter novamente
  • Com o ID, se o tipo de ID mudar, digamos int -> long, isso exigiria alterações gerais, com possibilidade de erros. (Cortes: https://softwareengineering.stackexchange.com/a/284734/145808 )

Profissionais do uso do ID:

  • Na maioria das vezes, não é necessário o objeto real, apenas o uniqueid faria, portanto, ter o ID economiza tempo para obtê-lo da persistência.

Uma mistura dessas técnicas, até onde posso ver, teria apenas contras de ambos e profissionais de nenhuma das duas.

Dado que este é um problema definido concretamente, espero que haja respostas objetivas e não os tipos "eu acho" ou "eu gosto" ... :-).

EDIT: Contexto sobre a sugestão de um comentarista. A única restrição é uma linguagem de tipo estaticamente. O aplicativo é um aplicativo de uso geral, embora tenha prazer em obter respostas com base em cenários de uso específicos.

EDIT: Um pouco mais de contexto. Digamos que eu tenha um sistema de gerenciamento de livros. Meu modelo é:

Book: { id: Int, isbn: String, donatedBy: Member, borrowedBy: Member }
Member: {id: Int, name: String}

Table- wishlist: { isbn: String, memberId: Int}
Table- borrows:  { id: Int, memberId: Int}  

Method: 
isInWishList( book: Book, member: Member ) vs isInWishList( bookId: Int,  memberId: Int)

handleBorrowRequest( memberId: Int, book: Book ){ 
   //the login system doesn't actually get the entire member structure, only the member id. I don't need the full member yet. can run a query to verify that the memberId has less than max allowed books for borrowing. 
}

Por que eu gostaria de manter apenas o id e não o membro completo? Digamos que quando recebo informações sobre o livro, raramente preciso saber quem o doou ou pediu emprestado. Obter as informações completas para preencher os campos doados e emprestados é um exagero.

Claro, esses são apenas meus pontos. Tenho certeza de que estou perdendo as coisas, então queria saber quais são os pontos a favor / contra.

0fnt
fonte
Você poderia editar sua pergunta com um pouco mais de contexto? Não está claro qual é o cenário. Eu acho que você está falando sobre objetos que são gerenciados por um sistema ORM (como o Hibernate) e que por "mini-classe" você quer dizer um objeto proxy de carregamento lento.
Darien
2
Possivelmente relacionado? programmers.stackexchange.com/questions/284634/…
hjk 28/05
Adicionando como comentário aqui (até que eu possa ter contexto suficiente para expandir para uma resposta completa): A menos que você esteja codificando valores de ID em seu código (um grande não-não), você ainda precisará carregar seus valores de ID de algum lugar certo? "Na maioria das vezes" também é bastante vago IMHO ...
hjk 28/05
@hjk Adicionado mais contexto, obrigado. Você tem razão - ainda precisa carregar valores de ID de algum lugar. No entanto, não se trata de não 'tocar' o banco de dados, mas de minimizar o uso e a largura de banda do banco de dados. Posso obter identificações de todos aqueles que querem emprestar um livro, mas, na verdade, obter todos os detalhes seria desnecessário.
0fnt
Parece que isso dependeria de várias coisas: (1) se os casos de uso exigem os atributos reais (colunas) ou apenas o ID, que depende dos próprios casos de uso; (2) se você usa algumas técnicas de armazenamento em cache ou de aprimoramento de desempenho que tornariam a consulta de identificação de atributos sem esforço e se seus casos de uso seriam pesados ​​o suficiente para interromper esses esquemas de armazenamento em cache.
rwong 28/05

Respostas:

8

Dependendo do ambiente e do contexto do problema, a necessidade de acessar o banco de dados pode ser atenuada e, como resultado (IMO), o argumento para usar apenas os IDs é perdido.

Por exemplo, o Doctrine2 ORM do PHP EntityManager::getReferenceproduz um proxy carregado lentamente para uma entidade usando o ID fornecido; Não sei quantos outros ORMs fazem isso, mas corresponde aproximadamente à noção de uma "mini-classe" mencionada. Para a maioria das intenções e propósitos, essa é uma entidade totalmente hidratada, para que você possa distribuí-la e usá-la como qualquer outra.

O único caso em que não é é quando recebe um ID inválido e, nesse caso, você deseja ir ao banco de dados para validar; além disso, para reduzir o custo de obtenção de entidades, a maioria dos ORMs tem a funcionalidade de cache (primeiro e segundo nível) disponível de alguma forma.

Depois que reduzimos a necessidade e o custo de acessar o banco de dados, ficamos em grande parte com profissionais de usar a entidade como parâmetro:

  1. Digite segurança.
  2. Menos conhecimento exigido pelo chamador. Um desenvolvedor seis meses depois da linha não pode passar por engano o ID da entidade errada.
  3. Custo de refatoração reduzido. Se um método foi alterado para exigir 2 informações da entidade, não há custo em alterar o chamador para fornecê-lo, presumindo que o chamador tenha a entidade totalmente hidratada para começar.
  4. Menos validação necessária por método. Muitos métodos podem assumir que a entidade fornecida existe no banco de dados (porque veio do ORM) e, portanto, não precisa mais validar o ID.
  5. (Potencialmente) menos chamadas ao banco de dados. Se você estiver passando IDs para 3 métodos, e cada um deles precisar validá-lo ou buscar mais dados sobre a entidade, isso significa 3 vezes mais chamadas ao banco de dados do que 1 método para buscar a entidade e 2 operando nela.

Obviamente, há casos em que se pode querer passar apenas um ID: por exemplo, ao cruzar um limite de contexto limitado (na fala do design orientado a domínio). Se considerarmos dois contextos limitados com noções diferentes do que compõe uma conta (por exemplo, finanças x relações com clientes), não podemos passar a conta do contexto A para o contexto B. Em vez disso, um ID de conta (no idioma do domínio) pode ser passado.

Andy Hunt
fonte
O armazenamento em cache não seria bem dimensionado com várias instâncias do aplicativo em execução. Eu tenho meu próprio cache no local. Os pontos são excelentes.
0fnt
Acho que mais um ponto contra os IDs é que o uso de ID implica chamar persistência além da validação, para realmente obter o objeto também.
0fnt
3

Para esse tipo de pergunta, eu delego para a solução que melhor comunica minha intenção como programador e facilita o trabalho de "Future Greg".

O uso do objeto como argumento torna mais fácil acertar a chamada do método daqui a seis meses e esqueci os detalhes dessa aplicação. Para desenvolver o seu "este livro está no exemplo da lista de desejos dessa pessoa", digamos que o isInWishListmétodo realmente precise apenas comparar os IDs inteiros de livros e membros. Na realidade, esse método só precisa de dois dados para realizar seu trabalho. Agora, vamos dar um passo atrás neste método único e examinar todo o sistema de software. Duvido que exija apenas um ID do livro e um ID do membro para operar. Você estará retirando o livro e o membro completos do banco de dados de qualquer maneira antes mesmo de chamar, isInWishListporque é provável que precise fazer algo mais do que apenas chamar esse método. Talvez você faça, mas na maioria das vezes você precisará fazer mais.

Dado isso, você realmente não sofrerá uma penalidade de desempenho ao buscar e mapear os dois objetos na íntegra no banco de dados. Teoricamente, você precisará de menos chamadas ao banco de dados, mas na prática descobrirá que isso simplesmente não é verdade. Se você passar esses IDs como parâmetros na string de consulta de volta ao servidor, sim, será necessário buscar a lista de desejos no banco de dados. Mas essa é uma visão muito restrita de todo o seu aplicativo. Haverá cenários quando você estiver executando outras operações, além de verificar uma lista de desejos - por exemplo, adicionando um livro a uma lista de desejos. Você não deseja adicionar itens duplicados.

Ir para a solução que é mais fácil para um novo programador, ou o seu futuro auto entender, o que é passar o Booke Memberobjetos. Só depois de encontrar um real problema de desempenho que você deve realmente preocupação-se com apenas passando as identificações.

Ou então, podemos lembrar que linguagens de tipo estaticamente, como Java, oferecem sobrecarga de método, para que você possa comer seu bolo e comê-lo também:

public boolean isInWishList(int bookId, int memberId) {
    // ...
}

public boolean isInWishList(Book book, Member member) {
    return isInWishList(book.getId(), member.getId());
}

A resposta real para essa pergunta é escrever os dois métodos, chamar a versão somente número inteiro da versão fortemente tipada e Bob é seu tio.

Greg Burghardt
fonte