A entidade de domínio está violando o princípio de responsabilidade única?

13

A responsabilidade única (motivo da mudança) de uma entidade deve ser identificar-se de forma exclusiva, ou seja, sua responsabilidade deve ser identificável.

Livro DDD de Eric Evan, pág. 93:

A responsabilidade mais básica das Entidades é estabelecer continuidade para que o comportamento possa ser claro e previsível. Eles fazem isso melhor se forem mantidos livres. Em vez de focar nos atributos ou mesmo no comportamento, reduza a definição do objeto Entity às características mais intrínsecas, particularmente aquelas que o identificam ou são comumente usadas para encontrá-lo ou combiná-lo. Adicione apenas comportamentos essenciais ao conceito e aos atributos exigidos por esse comportamento.

Além disso, procure remover comportamentos e atributos em outros objetos associados à Entidade principal. Além dos problemas de identidade, as Entidades tendem a cumprir suas responsabilidades coordenando as operações dos objetos que possuem.

1

... reduza a definição do objeto ENTITY até as características mais intrínsecas, particularmente aquelas que o identificam ou são comumente usadas para encontrá-lo ou combiná-lo. Adicione apenas comportamentos essenciais ao conceito ...

Depois que uma entidade recebe um ID exclusivo , sua identidade é estabelecida e, portanto, eu presumo que essa entidade não precisa de nenhum comportamento para manter sua identidade ou ajudá-la a se identificar . Portanto, não entendo a que tipo de comportamento o autor se refere (além de finde match operações ) com " comportamento essencial ao conceito "?

2)

... reduza a definição do objeto ENTITY até as características mais intrínsecas, particularmente aquelas que o identificam ou são comumente usadas para encontrá-lo ou combiná-lo. ... Além disso, procure remover comportamentos e atributos em outros objetos associados à ENTITY principal.

Portanto, qualquer comportamento que não ajude a identificar a entidade, mas ainda assim caracterizá-lo como uma característica intrínseca dessa entidade (por exemplo, latir é intrínseco aos cães, voar é intrínseco aos aviões, pôr ovos é intrínseco aos pássaros .. .), deve ser colocado em outros objetos associados a essa entidade (exemplo: devemos colocar o comportamento de latido em um objeto associado a uma entidade de cachorro)?

3)

Além disso, procure remover comportamentos e atributos em outros objetos associados à ENTITY principal.

a) MyEntitydelega responsabilidades A_respe B_respobjetos ae b, respectivamente.

Mesmo que a maioria dos A_respe B_resptrabalho é feito por ae bcasos, os clientes ainda são servidos A_respe B_respmeio MyEntity, o que significa que a partir da perspectiva do cliente as duas responsabilidades pertencem MyEntity. Assim, isso não significa que MyEntitytambém tem A_respe B_respresponsabilidades e, como tal, está violando o SRP ?

b) Mesmo se assumirmos que A_respe B_respnão pertencem a MyEntity, MyEntityainda tem a responsabilidade AB_respde coordenar as operações de objetos ae b. Portanto, não MyEntityviola o SRP, uma vez que, no mínimo, tem duas responsabilidades - identificar-se exclusivamente e também AB_resp?

class MyEntity
{
    private A a = ...
    private B b = ...


    public A GetA()
    { ... }

    public B GetB()
    { ... }

    /* coordinates operations of objects a and b */
    public int AworkB()
    { ... }
}

/* A encapsulates a single responsibility resp_A*/
/* A is value object */
class A
{ ... }

/* B encapsulates a single responsibility resp_B*/
/* B is value object */
class B
{ ... }

ATUALIZAR:

1

O comportamento neste contexto refere-se ao comportamento semântico. Por exemplo, uma propriedade em uma classe (ou seja, atributo em um objeto de domínio) que é usada para identificar exclusivamente que ela possui um comportamento. Enquanto isso não é representado no código diretamente. O comportamento esperado é que não haverá valores duplicados para essa propriedade.

Portanto, no código, quase nunca precisaríamos realmente implementar um comportamento (ou seja, uma operação) que de alguma forma manteria a identidade da entidade, pois, como você explicou, esse comportamento só existe como um conceito em um modelo de domínio (na forma de um atributo de ID de uma entidade), mas quando convertemos esse atributo de ID em código, parte de sua semântica é perdida (ou seja, a parte que garante implicitamente que o valor do ID é único é perdida)?

2)

Além disso, uma propriedade como Age não tem contexto fora de uma Entidade da Pessoa e, como tal, não faz sentido mudar para um objeto diferente ... No entanto, essas informações podem ser facilmente armazenadas em um local separado pelo qual o identificador exclusivo, portanto, o referência confusa ao comportamento. A idade pode ser um valor carregado preguiçoso.

a) Se a Agepropriedade é carregada preguiçosamente, podemos chamá-la de comportamento, mesmo que semanticamente Ageseja apenas um atributo?

3)

Você poderia facilmente ter operações específicas para Endereço, como verificação de que é um endereço válido. Você pode não saber disso em tempo de design, mas todo esse conceito é dividir os objetos em suas menores partes

Embora eu concorde que perderíamos o contexto mudando Agepara um objeto diferente, o contexto não seria perdido se DateOfBirthmovermos a propriedade para um objeto diferente, mas geralmente não o movemos.

Qual é a principal razão pela qual nos mudaríamos Addresspara outro objeto, mas não DateOfBirth? Porque DateOfBirthé mais intrínseco à Personentidade ou porque há menos chances de que em algum lugar no futuro possamos precisar definir operações específicas DateOfBirth?

4. Devo dizer que ainda não sei se MyEntitytambém tem A_respe B_respresponsabilidades e por que MyEntitytambém AB_respnão é considerado uma violação do SRP

EULERFX

1)

Os comportamentos aos quais o autor está se referindo são comportamentos associados à entidade. Esses são os comportamentos que modificam o estado da entidade

a) Se eu entendi direito, você está dizendo que a entidade deve conter apenas os comportamentos que modificam seus atributos (ou seja, seu estado )?

b) E quanto aos comportamentos que não necessariamente modificam o estado da entidade , mas ainda são considerados uma característica intrínseca dessa entidade (exemplo: latir seria uma característica intrínseca de uma Dogentidade, mesmo que não modificasse Estado do cão )? Devemos incluir esses comportamentos em uma entidade ou devem ser movidos para outros objetos?

2)

Quanto a mudar o comportamento para outros objetos, o autor está se referindo especificamente a objetos de valor.

Embora minha citação não a inclua, o autor menciona no mesmo parágrafo que, em alguns casos, comportamentos (e atributos ) também serão transferidos para outras entidades (embora eu compreenda os benefícios de mudar comportamentos para VOs)

3) Supondo que MyEntity(veja a pergunta 3. no meu post original) não viole o SRP, diríamos que uma responsabilidade de MyEntityé, entre outras coisas, também composta por:

uma. A_resp + B_resp + AB_resp ( AB_respcoordena objetos ae b)

ou

b. AB_resp + delegar A_respe B_respaos objetos ( ae b) associados a MyEntity?

4) Livro DDD de Eric Evan, pág. 94:

Identificação do cliente é o único identificador da ENTIDADE do cliente (figura 5.5), mas o número e o endereço do telefone costumam ser usados ​​para encontrar ou corresponder a um cliente. O nome não define a identidade de uma pessoa, mas é frequentemente usado como parte dos meios para determiná-la.

Neste exemplo, os atributos de telefone e endereço foram movidos para o Cliente, mas em um projeto real, essa escolha dependeria de como os clientes do domínio normalmente são correspondidos ou distinguidos. Por exemplo, se um Cliente tiver muitos números de telefone para diferentes finalidades, o número de telefone não está associado à identidade e deve permanecer no Contato de Vendas.

a)

Identificação do cliente é o único identificador da ENTIDADE do cliente (figura 5.5), mas o número e o endereço do telefone costumam ser usados ​​para encontrar ou corresponder a um cliente. O nome não define a identidade de uma pessoa, mas é frequentemente usado como parte dos meios para determiná-la.

A citação afirma que apenas os atributos associados à identidade devem permanecer em uma entidade . Suponho que autor significa que a entidade deve conter apenas os atributos que costumam ser usados ​​para encontrar ou corresponder a essa entidade , enquanto TODOS os outros atributos devem ser movidos?

b) Mas como / onde outros atributos devem ser movidos? Por exemplo (suposição aqui é que atributo de endereço não é usado para encontrar ou corresponder Customer e, portanto, queremos mover atributo de endereço fora do Customer):

se em vez de ter Customer.Address(do tipo string) criamos uma propriedade Customer.Addressdo tipo Address, movemos o atributo de endereço para um objeto VO associado (que é do tipo Address) ou diríamos que Customerainda contém o atributo de endereço ?

c)

Neste exemplo, os atributos de telefone e endereço foram movidos para o Cliente, mas em um projeto real, essa escolha dependeria de como os clientes do domínio normalmente são correspondidos ou distinguidos. Por exemplo, se um Cliente tiver muitos números de telefone para diferentes finalidades, o número de telefone não está associado à identidade e deve permanecer no Contato de Vendas.

O autor não está errado aqui, já que se assumirmos cada um dos muitos números de telefone de contato que Customerpertencem apenas a esse particular Customer, eu diria que esses números de telefone estão associados à identidade tanto quanto quando Customerapenas um número de telefone ?

5)

O motivo pelo qual o autor sugere descartar a entidade é que, quando alguém cria inicialmente uma entidade Cliente, há uma tendência a preenchê-la com qualquer atributo que se possa considerar associado a um cliente. Essa é uma abordagem centrada em dados que negligencia os comportamentos que levam a um modelo de domínio anêmico.

Off topic, mas eu pensei anêmicos modelo de domínio resultados de mover comportamento fora de uma entidade , enquanto que o seu exemplo é preencher uma entidade com muitos atributos , o que resultaria em Customerter muita comportamento (uma vez que provavelmente também incluir Customernos comportamentos que modificar esses atributos adicionais ) e, portanto, violar o SRP?

obrigado

EdvRusj
fonte
2
eu recomendaria altamente a série de vídeos de código limpo de robert martins, cleancoders.com Ele detalha como os diferentes princípios podem causar problemas ou equilibrar-se. caso contrário, acho que parte da fórmula do seu exemplo seria o período de tempo em que o objeto Person está preocupado. se por um breve período, como uma compra, o endereço de cobrança usado para a compra faria parte dela e imutável. se for para uma conta da Biblioteca, o endereço deve poder ser alterado.
cartalot 13/05
2
Eu acho que esta questão pode ser violar a SRP ...;)
IntelliData

Respostas:

6

O comportamento neste contexto refere-se ao comportamento semântico. Por exemplo, uma propriedade em uma classe (ou seja, atributo em um objeto de domínio) que é usada para identificar exclusivamente que ela possui um comportamento. Enquanto isso não é representado no código diretamente. O comportamento esperado é que não haverá valores duplicados para essa propriedade. Algo como um Endereço que pode ter sua própria identidade, mas não existe fora do contexto de uma Entidade Pessoa, ainda deve ser movido para seu próprio objeto. Assim, promovendo a Entidade em uma Raiz Agregada.

Além disso, uma propriedade como Age não tem contexto fora de uma Entidade Pessoa e, como tal, não faz sentido mudar para um objeto diferente. O contexto seria perdido e, portanto, você pode determinar com segurança que é um valor essencial para a Entidade da Pessoa. Não foi possível localizar o valor caso contrário. No entanto, essas informações podem ser facilmente armazenadas em um local separado do identificador exclusivo, daí a referência confusa ao comportamento . A idade pode ser um valor carregado preguiçoso.

Então, para responder sua pergunta. Não, não viola o Princípio da Responsabilidade Única. É lamentável afirmar que uma Pessoa deve ter apenas material de pessoa, e não algo como Endereço, que é mais complexo e relacionado a uma pessoa, deve existir como sua própria entidade.

Você poderia facilmente ter operações específicas para Endereço, como verificação de que é um endereço válido. Você pode não saber disso no momento do design, mas esse conceito é dividir os objetos em suas partes menores, para que algo assim seja relativamente simples quando feito após o fato.

Atualização: 1) Na maioria dos casos, essa validação de identidade é feita ao salvar um objeto em um armazenamento de dados. O que significa que o código que representa a validação da entidade existe, mas existe em outro lugar. Geralmente existe com o código responsável pela emissão do valor da identidade. É por isso que afirmo que a exclusividade não é representada diretamente no código da entidade.

2) A afirmação correta seria que Ageé um atributo que possui comportamento. Você precisaria documentar o fato de que o Age é carregado preguiçosamente para que um desenvolvedor que consome essa propriedade possa tomar uma decisão precisa sobre como consumir essa propriedade

3) DateOfBirthgeralmente é um objeto diferente; Um objeto de data que já possui operações predefinidas. Em alguns idiomas, o objeto date já possui um modelo de domínio inteiro definido. Por exemplo, em c #, você pode especificar o fuso horário, se a data for UTC, adicionar e subtrair datas para obter um intervalo de tempo. Portanto, sua suposição sobre a mudança DateOfBirthseria correta.

4) Se a única coisa que MyEntityfaz é delegação e cooridação, então não, isso não viola o SRP. Sua única responsabilidade é delegar e coordenar e é referido como o padrão Facade

Charles Lambert
fonte
Você poderia olhar para a atualização que fiz?
EdvRusj
Atualizado minha resposta
Charles Lambert
4

Muito boa pergunta. O SRP não deve ser tomado tão literariamente. A identificação / pesquisa não é de responsabilidade da entidade no que diz respeito ao SRP. Outra pessoa é responsável por fornecer um ID (ou seja, a loja) e procurá-lo (ou seja, o Repositório ).

O objetivo principal de uma entidade é representar os conceitos descobertos pelo modelo. A única diferença entre uma Entidade e um Objeto de Valor é que a Entidade tem significado além de seus atributos não identificados. Por exemplo, se uma pessoa muda seu nome, ele ainda é a mesma pessoa, apenas com um nome diferente.

Michael Brown
fonte
1

Depois que uma entidade recebe um ID exclusivo, sua identidade é estabelecida e, portanto, eu presumo que essa entidade não precisa de nenhum comportamento para manter sua identidade ou ajudá-la a se identificar. Portanto, não entendo a que tipo de comportamento o autor se refere (além de operações de localização e correspondência) com "comportamento essencial ao conceito"?

Se a identidade for estabelecida, sim, a entidade não precisa mais nada para ser identificado. Os comportamentos aos quais o autor está se referindo são comportamentos associados à entidade. Esses são os comportamentos que modificam o estado da entidade. Por exemplo, uma Customerentidade pode ter um MakePreferredcomportamento. O motivo pelo qual o autor sugere retirar a entidade é que, quando alguém cria uma Customerentidade inicialmente , existe uma tendência a preenchê-la com qualquer atributo que se possa pensar em estar associado a um cliente. Essa é uma abordagem centrada em dados que negligencia os comportamentos que levam a um modelo de domínio anêmico.

Quanto a mudar o comportamento para outros objetos, o autor está se referindo especificamente a objetos de valor. A razão pela qual é uma boa ideia mudar o comportamento para os VOs é porque os VOs são geralmente "menores" do que as entidades, portanto, mais focados. Além disso, aspectos como imutabilidade e fechamento de operações simplificam o raciocínio sobre o código, além de torná-lo mais sólido .

Juntamente com os VOs, uma entidade serve como uma espécie de âncora que coordena os vários VOs que implementam seu comportamento.

Com relação ao SRP, sua confusão não é injustificada. Um problema com a implementação estereotipada de OOP de entidades é a fusão de identidade e estado. De fato, de uma perspectiva comportamental, a identidade não tem nada a ver com comportamentos. Em outras palavras, a identidade de uma entidade não é necessária para nenhum de seus comportamentos. Existem implementações em que essa confusão é eliminada, como o AggregateSource ou uma abordagem funcional que descrevo aqui .

O outro problema é que, até certo ponto, o SRP pode ser uma medida qualitativa. Qualquer pessoa pode apresentar uma definição de responsabilidade única que alguma classe viole. Pode-se dizer que a responsabilidade da entidade é implementar os comportamentos exigidos dessa entidade. Nesse sentido, ele tem uma única responsabilidade. Além disso, quando uma entidade delega comportamentos aos VOs constituintes, não está violando o SRP. O SRP não proíbe a composição do tipo. Ele alerta para reduzir o acoplamento entre os objetos a um mínimo absoluto, manter as interfaces o mais nítidas possível, etc.

ATUALIZAR

a) Se eu entendi direito, você está dizendo que a entidade deve conter apenas os comportamentos que modificam seus atributos (ou seja, seu estado)?

Sim, embora haja exceções ...

b) E os comportamentos que não necessariamente modificam o estado da entidade, mas ainda são considerados uma característica intrínseca dessa entidade (exemplo: latir seria uma característica intrínseca de uma entidade Dog, mesmo que não o fizesse) modificar o estado do cão)? Devemos incluir esses comportamentos em uma entidade ou devem ser movidos para outros objetos?

É aceitável que as entidades contenham métodos de fábrica para criar instâncias de entidades que sejam efetivamente entidades filhas, mas onde as referências a objetos não são usadas para travessia. Nesse caso, a entidade filha precisa ser persistida pelo serviço de aplicativo. O serviço de aplicativo usa a entidade pai para construir a entidade filho.

3) Supondo que o MyEntity (consulte a pergunta 3. no meu post original) não viole o SRP, diríamos que uma responsabilidade do MyEntity está entre outras coisas, também composta por:

Você está encarando a responsabilidade da perspectiva da implementação. Em vez disso, considere a entidade como uma espécie de caixa preta com responsabilidades. O modo como ele lida com isso não é do seu interesse como alguém olhando de fora. O particionamento de responsabilidades entre VOs ou mesmo outras entidades é uma preocupação de implementação.

A citação afirma que apenas os atributos associados à identidade devem permanecer em uma entidade. Suponho que autor significa que a entidade deve conter apenas os atributos que costumam ser usados ​​para encontrar ou corresponder a essa entidade, enquanto TODOS os outros atributos devem ser movidos?

Mais especificamente, atributos que não são necessários para o comportamento nem procuram não devem fazer parte da entidade. Porque se importar? Além disso, com algo como o padrão de modelo de leitura , as entidades não precisam de nada além dos atributos exigidos para o comportamento.

se, em vez de ter Customer.Address (do tipo string), criarmos uma propriedade Customer.Address do tipo Address, movemos o atributo address para um objeto VO associado (que é do tipo Address) ou diríamos que o Cliente ainda contém o endereço atributo?

Sim, com efeito, não há diferença entre um endereço de string ou um endereço de VO do endereço.

O autor não está errado aqui, já que se assumirmos cada um dos muitos números de telefone de contato que o Cliente pertence apenas a esse Cliente em particular, eu diria que esses números de telefone estão associados à identidade tanto quanto quando o Cliente tinha apenas um número de telefone?

Eu não sou 100% na intenção do autor, mas acho que ele está apenas descrevendo como os requisitos de pesquisa de entidade podem alterar como a entidade e seus VOs correspondentes são estruturas.

Fora do tópico, mas achei que o modelo de domínio anêmico resulta da mudança de comportamento para fora de uma entidade, enquanto o seu exemplo está preenchendo uma entidade com muitos atributos, o que resultaria em muito comportamento do Cliente (já que provavelmente incluiríamos no Cliente o comportamentos que modificam esses atributos adicionais) e, portanto, violam o SRP?

Muitos atributos não significam muito comportamento. De fato, geralmente sugere o oposto. Muitos atributos com getters e setters, mas nenhum comportamento de encapsulamento.

eulerfx
fonte
Eu fiz uma atualização
EdvRusj
1

TL; DR: você está pensando demais. No entanto, eu me diverti pensando demais junto com você. Então aperte o cinto ....

A responsabilidade única (motivo da mudança) de uma entidade deve ser identificar-se de forma exclusiva, ou seja, sua responsabilidade deve ser identificável.

Não, isso não está certo. A responsabilidade única de uma entidade é a continuidade.

A identidade é uma conseqüência emergente da continuidade. Modelar a identidade como uma ideia separável é uma otimização de desempenho.

Aqui está um exemplo: o patrono de um restaurante dá o carro ao manobrista. Uma hora depois, um cliente do restaurante pede o carro. O manobrista deveria dar?

É fácil dizer que o manobrista deve entregar o carro se o cliente for o "mesmo". Mas o que isso realmente significa? A maneira correta de determinar isso é começar com o cliente "agora" e pesquisar para trás na história desse cliente para ver se a entrega do carro ao manobrista faz parte dessa história.

Na verdade, não podemos fazer isso, é claro. Temos problemas para rastrear nossa própria história com precisão, não importa a história de algo que não estava conosco o tempo todo. Então, em vez de usar a história do consumidor, tomamos atalhos. O usuário possui o canhoto do ticket que tem o mesmo número da tag atualmente vinculada às chaves? A carteira de motorista na carteira do usuário corresponde ao nome do título na DMV, a imagem na carteira de motorista se assemelha à face do usuário. Etc.

Em resumo: em vez de verificar o histórico do usuário, verificamos o estado atual do usuário, para ver se o estado atual é consistente com um histórico que abrange o período entre a chegada do carro e a solicitação de devolução.

Ao modelar entidades, usamos uma otimização análoga. Damos a todas as entidades as responsabilidades comuns de

  1. Garantir que o início do histórico inclua uma atribuição de um identificador imutável ao estado do objeto
  2. Garantir que o próximo estado sempre inclua uma cópia fiel do identificador do estado anterior.

Não estou descrevendo uma segunda responsabilidade da entidade aqui; a entidade ainda é responsável pela continuidade - certificando-se de que a história seja uma narrativa consistente. As responsabilidades do identificador são apenas um subconjunto que é comum a todas as entidades.

Ainda não temos nenhuma aplicação de exclusividade. Isso não é possível em uma única entidade, porque a exclusividade requer acesso ao estado de todas as entidades; onde uma única entidade só tem acesso próprio.

Mais uma vez, verificar todos os identificadores sempre não é prático; portanto, satisfazemos a exclusividade da maneira mais fácil: o código que gera o próximo identificador nunca deve se repetir.

No final, isso significa que podemos verificar a continuidade testando a equivalência de duas partes diferentes de estado na memória, poupando muito trabalho ao tentar consultar gráficos acíclicos.

Você também parece ter confundido o Princípio da Responsabilidade Única (que é realmente uma boa ideia) com um princípio de responsabilidade atômica. Decompor uma responsabilidade em partes menores e mais facilmente gerenciadas é compatível com o SRP.

VoiceOfUnreason
fonte
-3

Bem, depende de como você deseja vê-lo.

Outra maneira é: "O princípio de responsabilidade única está violando a entidade de domínio?"

Ambos são diretrizes. Não há "princípio" em nenhum lugar do design de software. No entanto, existem bons e maus designs. Ambos os conceitos podem ser usados ​​de diferentes maneiras, para obter um bom design.

h bob
fonte
Downvotes inexplicáveis ​​== SRP fanboys
h bob