¿La entidad de dominio está violando el principio de responsabilidad única?


13

La responsabilidad única (razón para cambiar) de una entidad debe ser identificarse de manera única, en otras palabras, su responsabilidad debe ser localizable.

El libro DDD de Eric Evan, pág. 93:

La responsabilidad más básica de las entidades es establecer la continuidad para que el comportamiento pueda ser claro y predecible. Lo hacen mejor si se mantienen libres. En lugar de enfocarse en los atributos o incluso en el comportamiento, elimine la definición del objeto Entity hasta las características más intrínsecas, particularmente aquellas que lo identifican o se usan comúnmente para encontrarlo o combinarlo. Agregue solo el comportamiento que sea esencial para el concepto y los atributos que requiere ese comportamiento.

Más allá de eso, busque eliminar el comportamiento y los atributos en otros objetos asociados con la Entidad central. Más allá de los problemas de identidad, las Entidades tienden a cumplir con sus responsabilidades coordinando las operaciones de los objetos que poseen.

1)

... elimine la definición del objeto ENTITY hasta las características más intrínsecas, particularmente aquellas que lo identifican o se usan comúnmente para encontrarlo o combinarlo. Agregue solo un comportamiento que sea esencial para el concepto ...

Una vez que a una entidad se le asigna una identificación única , se establece su identidad y, por lo tanto, supongo que dicha entidad no necesita ningún comportamiento para mantener su identidad o ayudarla a identificarse . Por lo tanto, no entiendo qué tipo de comportamiento es el autor se refiere a (además findy match operaciones ) con " el comportamiento que es esencial para el concepto "?

2)

... elimine la definición del objeto ENTITY hasta las características más intrínsecas, particularmente aquellas que lo identifican o se usan comúnmente para encontrarlo o combinarlo. ... Más allá de eso, busca eliminar el comportamiento y los atributos en otros objetos asociados con la ENTIDAD central.

Entonces, cualquier comportamiento que no ayude a identificar la entidad, pero aún así caracterizaríamos ese comportamiento como una característica intrínseca de esa entidad (es decir, el ladrido es intrínseco a los perros, volar es intrínseco a los aviones, poner huevos es intrínseco a las aves ... .), debe colocarse en otros objetos asociados con esa entidad (ejemplo: debemos poner el comportamiento de ladrar en un objeto asociado con una entidad de perro)?

3)

Más allá de eso, busque eliminar el comportamiento y los atributos en otros objetos asociados con la ENTIDAD central.

a) MyEntitydelega responsabilidades A_respy B_respobjetos ay b, respectivamente.

A pesar de que la mayor parte de A_respy B_resptrabajo es realizado por ay bcasos, los clientes están siendo servidos A_respy B_respa través de MyEntity, lo que significa que desde la perspectiva del cliente las dos responsabilidades pertenecen a MyEntity. Por lo tanto, no significa eso MyEntitytambién tiene A_respy B_respresponsabilidades y, como tal, está violando SRP ?

b) Incluso si asumimos eso A_respy B_respno pertenecemos MyEntity, MyEntitytodavía tiene la responsabilidad AB_respde coordinar las operaciones de los objetos ay b. Entonces, ¿no MyEntityviola SRP ya que como mínimo tiene dos responsabilidades : identificarse de manera única y también 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
{ ... }

ACTUALIZAR:

1)

El comportamiento en este contexto se refiere al comportamiento semántico. Por ejemplo, una propiedad en una clase (es decir, un atributo en un objeto de dominio) que se usa para identificarla de forma exclusiva tiene un comportamiento. Si bien esto no está representado en el código directamente. El comportamiento esperado es que no habrá valores duplicados para esa propiedad.

Entonces, en el código, casi nunca necesitaríamos implementar realmente un comportamiento (es decir, una operación) que de alguna manera mantendría la identidad de la entidad, ya que como usted explicó, dicho comportamiento solo existe como un concepto en un modelo de dominio (en forma de un atributo ID de una entidad), pero cuando traducimos este atributo de ID en código, se pierde parte de su semántica (es decir, se pierde la parte que implícitamente se asegura de que el valor de ID sea único).

2)

Además, una propiedad como Age no tiene contexto fuera de una Entidad Persona y, como tal, no tiene sentido moverse a un objeto diferente ... Sin embargo, esa información podría almacenarse fácilmente en una ubicación separada que el identificador único, de ahí el referencia confusa al comportamiento. La edad podría ser un valor cargado flojo.

a) Si la Agepropiedad está cargada de manera diferida, ¿podemos llamarlo un comportamiento, aunque semánticamente Agesea ​​solo un atributo?

3)

Fácilmente podría tener operaciones específicas de Dirección, como la verificación de que es una dirección válida. Puede que no lo sepas en el momento del diseño, pero todo este concepto es descomponer los objetos en sus partes más pequeñas.

Si bien estoy de acuerdo en que perderíamos el contexto al movernos Agea un objeto diferente, el contexto no se perdería si moviéramos la DateOfBirthpropiedad a un objeto diferente, pero generalmente no lo movemos.

¿Cuál es la razón principal por la que nos moveríamos Addressa otro objeto, pero no DateOfBirth? ¿Porque DateOfBirthes más intrínseco a la Personentidad o porque hay menos posibilidades de que en algún momento en el futuro necesitemos definir operaciones específicas para DateOfBirth?

4. debo decir que todavía no sé si MyEntitytambién tiene A_respy B_respresponsabilidades y por eso MyEntitytambién haber AB_respno se considera una violación de SRP

EULERFX

1)

Los comportamientos a los que se refiere el autor son comportamientos asociados con la entidad. Estos son los comportamientos que modifican el estado de la entidad.

a) Si te entiendo correctamente, ¿estás diciendo que la entidad solo debe contener esos comportamientos que modifican sus atributos (es decir, su estado )?

b) ¿Y qué hay de los comportamientos que no necesariamente modifican el estado de la entidad , pero que aún se consideran como una característica intrínseca de esa entidad (ejemplo: ladrar sería una característica intrínseca de una Dogentidad, incluso si no se modificara) Estado del perro )? ¿Deberíamos incluir estos comportamientos en una entidad o deberían trasladarse a otros objetos?

2)

En cuanto al comportamiento de movimiento a otros objetos, el autor se refiere específicamente a objetos de valor.

Aunque mi cita no lo incluye, pero el autor menciona en el mismo párrafo que en algunos casos los comportamientos (y los atributos ) también se trasladarán a otras entidades (aunque entiendo los beneficios de trasladar los comportamientos a las VO)

3) Suponiendo que MyEntity(vea la pregunta 3. en mi publicación original) no viola SRP, diríamos que una responsabilidad de MyEntityentre otras cosas también comprende:

a. A_resp + B_resp + AB_resp ( AB_respcoordina objetos ay b)

o

si. AB_resp + delegar A_respy B_respa objetos ( ay b) asociados con MyEntity?

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

CustomerID es el único identificador de la ENTIDAD del cliente (figura 5.5), pero el número de teléfono y la dirección a menudo se utilizan para encontrar o hacer coincidir un Cliente. El nombre no define la identidad de una persona, pero a menudo se usa como parte de los medios para determinarlo.

En este ejemplo, los atributos del teléfono y la dirección se trasladaron al Cliente, pero en un proyecto real, esa elección dependería de cómo los clientes del dominio suelen coincidir o distinguirse. Por ejemplo, si un cliente tiene muchos números de teléfono de contacto para diferentes propósitos, entonces el número de teléfono no está asociado con la identidad y debe permanecer con el contacto de ventas.

un)

CustomerID es el único identificador de la ENTIDAD del cliente (figura 5.5), pero el número de teléfono y la dirección a menudo se utilizan para encontrar o hacer coincidir un Cliente. El nombre no define la identidad de una persona, pero a menudo se usa como parte de los medios para determinarlo.

La cita establece que solo los atributos asociados con la identidad deben permanecer en una entidad . Supongo que autor significa que la entidad debe contener solo aquellos atributos que a menudo se usan para encontrar o coincidir con esta entidad , mientras que TODOS los demás atributos deben moverse.

b) ¿Pero cómo / dónde se deben mover otros atributos ? Por ejemplo (se supone que el atributo de dirección no se usa para buscar o coincidir Customer y, por lo tanto, queremos mover el atributo de dirección fuera de Customer):

si en lugar de tener Customer.Address(de tipo string) creamos una propiedad Customer.Addressde tipo Address, ¿movimos el atributo de dirección a un objeto VO asociado (que es de tipo Address) o diríamos que Customertodavía contiene el atributo de dirección ?

C)

En este ejemplo, los atributos del teléfono y la dirección se trasladaron al Cliente, pero en un proyecto real, esa elección dependería de cómo los clientes del dominio suelen coincidir o distinguirse. Por ejemplo, si un cliente tiene muchos números de teléfono de contacto para diferentes propósitos, entonces el número de teléfono no está asociado con la identidad y debe permanecer con el contacto de ventas.

El autor no está equivocado aquí, ya que si asumimos cada uno de los muchos números de teléfono de contacto que Customersolo pertenecen a ese particular Customer, entonces diría que estos números de teléfono están asociados con la identidad tanto como cuando Customersolo tenía un número de teléfono ?

5)

La razón por la que el autor sugiere desmantelar la entidad es que cuando uno crea inicialmente una entidad de Cliente, existe la tendencia de llenarla con cualquier atributo que se pueda pensar que está asociado con un cliente. Este es un enfoque centrado en los datos que pasa por alto los comportamientos que finalmente conducen a un modelo de dominio anémico.

Fuera de tema, pero pensé que el modelo de dominio anémico resulta de mover el comportamiento fuera de una entidad , mientras que su ejemplo es poblar una entidad con muchos atributos , lo que resultaría en Customerun comportamiento excesivo (ya que probablemente también incluiríamos en Customerlos comportamientos que modificar estos atributos adicionales ) y por lo tanto en violación de SRP?

Gracias


2
Recomiendo encarecidamente la serie de videos de código limpio de robert martins, cleancoders.com. Entra en gran detalle sobre cómo los diferentes principios pueden causar problemas o equilibrarse entre sí. de lo contrario, creo que parte de la fórmula para su ejemplo sería mirar el lapso de tiempo que le concierne al objeto Person. si es por un breve lapso de tiempo como una Compra, entonces la dirección de facturación utilizada para la compra sería parte de ella e inmutable. si es para una cuenta de la Biblioteca, entonces la dirección debería poder cambiar.
cartalot

2
Creo que esta pregunta podría estar violando el SRP ...;)
IntelliData

Respuestas:


6

El comportamiento en este contexto se refiere al comportamiento semántico. Por ejemplo, una propiedad en una clase (es decir, un atributo en un objeto de dominio) que se usa para identificarla de forma exclusiva tiene un comportamiento. Si bien esto no está representado en el código directamente. El comportamiento esperado es que no habrá valores duplicados para esa propiedad. Algo como una Dirección que puede tener su propia identidad, pero que no existe fuera del contexto de una Entidad Persona, aún debe trasladarse a su propio objeto. Promoviendo así la Entidad en una Raíz Agregada.

Además, una propiedad como Age no tiene contexto fuera de una Entidad Persona y, como tal, no tiene sentido moverse a un objeto diferente. El contexto se perdería y, por lo tanto, puede determinar con seguridad que es un valor esencial para la Entidad Persona. No podría localizar el valor de lo contrario. Sin embargo, esa información podría almacenarse fácilmente en una ubicación separada del identificador único, de ahí la referencia confusa al comportamiento . La edad podría ser un valor cargado flojo.

Entonces para responder a tu pregunta. No, no viola el principio de responsabilidad única. Es simple decir que una Persona debe tener solo cosas de persona, y no algo como Dirección, que es más complejo y relacionado con una persona, debe existir como su propia entidad.

Fácilmente podría tener operaciones específicas de Dirección, como la verificación de que es una dirección válida. Puede que no lo sepas en el momento del diseño, pero todo este concepto consiste en descomponer los objetos en sus partes más pequeñas para que algo como esto sea relativamente simple cuando se hace después del hecho.

Actualización: 1) En la mayoría de los casos, esta validación de identidad se realiza al guardar un objeto en un almacén de datos. Lo que significa que el código que representa la validación de la entidad existe, pero existe en otro lugar. Por lo general, existe con el código responsable de emitir el valor de identidad. Es por eso que afirmo que la unicidad no está representada directamente en el código de la entidad.

2) La declaración correcta sería que Agees un atributo que tiene comportamiento. Debería documentar el hecho de que Age tiene una carga lenta para que un desarrollador que consuma esa propiedad pueda tomar una decisión precisa sobre cómo consumir esa propiedad

3) DateOfBirthgeneralmente es un objeto diferente; Un objeto de fecha que ya tiene operaciones predefinidas en él. En algunos idiomas, el objeto de fecha ya tiene un modelo de dominio completo definido en él. Por ejemplo, en c # puede especificar la zona horaria, si la fecha es UTC, sume y reste fechas para obtener un intervalo de tiempo. Por lo tanto, su suposición sobre el movimiento DateOfBirthsería correcta.

4) Si lo único que MyEntityhace es delegación y cooridinación, entonces no, no viola SRP. Su única responsabilidad es delegar y coordinar y se conoce como el patrón Fachada.


¿Podrías mirar la actualización que hice?
EdvRusj

Actualicé mi respuesta
Charles Lambert,

4

Muy buena pregunta El SRP no debe tomarse tan literalmente. La identificación / búsqueda no es responsabilidad de la entidad en lo que respecta al SRP. Alguien más es responsable de darle una identificación (es decir, la tienda) y de buscarlo (es decir, el repositorio ).

El propósito principal de una entidad es representar los conceptos descubiertos por el modelo. La única diferencia entre una entidad y un objeto de valor es que la entidad tiene un significado más allá de sus atributos no identificativos. Por ejemplo, si una persona cambia su nombre, sigue siendo la misma persona, solo que con un nombre diferente.


1

Una vez que a una entidad se le asigna una identificación única, se establece su identidad y, por lo tanto, supongo que dicha entidad no necesita ningún comportamiento para mantener su identidad o ayudarla a identificarse. Por lo tanto, no entiendo a qué tipo de comportamiento se refiere el autor (además de las operaciones de búsqueda y comparación) con "comportamiento que es esencial para el concepto".

Si se establece la identidad, entonces sí, la entidad no necesita nada más para ser identificada. Los comportamientos a los que se refiere el autor son comportamientos asociados con la entidad. Estos son los comportamientos que modifican el estado de la entidad. Por ejemplo, una Customerentidad puede tener un MakePreferredcomportamiento. La razón por la que el autor sugiere desmantelar la entidad es que cuando uno crea inicialmente una Customerentidad, existe la tendencia de poblarla con cualquier atributo que se pueda pensar que está asociado con un cliente. Este es un enfoque centrado en datos que pasa por alto los comportamientos que finalmente conducen a un modelo de dominio anémico.

En cuanto al comportamiento de movimiento a otros objetos, el autor se refiere específicamente a objetos de valor. La razón por la que es una buena idea trasladar el comportamiento a las VO es porque las VO suelen ser "más pequeñas" que las entidades, por lo tanto, más enfocadas. Además, aspectos como la inmutabilidad y el cierre de operaciones simplifican el razonamiento sobre el código y lo hacen más SÓLIDO .

Junto con los VO, una entidad sirve como una especie de ancla que coordina los diversos VO que implementan su comportamiento.

Con respecto a SRP, su confusión no es injustificada. Un problema con la implementación estereotipada de OOP de entidades es la fusión de identidad y estado. De hecho, desde una perspectiva conductual, la identidad no tiene nada que ver con los comportamientos. En otras palabras, la identidad de una entidad no se requiere para ninguno de sus comportamientos. Hay implementaciones en las que se elimina esta combinación, como AggregateSource o un enfoque funcional que describo aquí .

El otro problema es que, hasta cierto punto, el SRP puede ser una medida cualitativa. Cualquiera puede llegar a una definición de responsabilidad única que viola alguna clase. Se puede decir que la responsabilidad de la entidad es implementar los comportamientos requeridos de esa entidad. En ese sentido, tiene una sola responsabilidad. Además, cuando una entidad delega comportamientos a VO constituyentes, no está violando SRP. SRP no prohíbe la composición de este tipo. Se advierte a uno para reducir el acoplamiento entre objetos a un mínimo absoluto, mantener las interfaces lo más desnudas posible, etc.

ACTUALIZAR

a) Si te entiendo correctamente, ¿estás diciendo que la entidad solo debe contener esos comportamientos que modifican sus atributos (es decir, su estado)?

Sí, aunque hay excepciones ...

b) ¿Y qué pasa con los comportamientos que no necesariamente modifican el estado de la entidad, pero que todavía se consideran como una característica intrínseca de esa entidad (ejemplo: ladrar sería una característica intrínseca de una entidad Dog, incluso si no fuera así) modificar el estado del perro)? ¿Deberíamos incluir estos comportamientos en una entidad o deberían trasladarse a otros objetos?

Es aceptable que las entidades contengan métodos de fábrica para crear instancias de entidades que serían efectivamente entidades secundarias, pero donde las referencias a objetos no se usan para el recorrido. En este caso, el servicio de aplicación debe persistir a la entidad secundaria. El servicio de aplicación utiliza la entidad principal para construir la entidad secundaria.

3) Suponiendo que MyEntity (vea la pregunta 3. en mi publicación original) no viola SRP, ¿podríamos decir que la responsabilidad de MyEntity está entre otras cosas también compuesta por:

Estás mirando la responsabilidad desde la perspectiva de implementación. En cambio, considere la entidad como una especie de caja negra con responsabilidades. Cómo maneja eso no es de su interés como alguien que mira desde afuera. La división de responsabilidades entre VO o incluso otras entidades es una preocupación de implementación.

La cita establece que solo los atributos asociados con la identidad deben permanecer en una entidad. Supongo que autor significa que la entidad debe contener solo aquellos atributos que a menudo se usan para encontrar o coincidir con esta entidad, mientras que TODOS los demás atributos deben moverse.

Más específicamente, los atributos que no son necesarios para el comportamiento ni para buscar no deberían ser parte de la entidad. ¿Por qué molestarse? Además, con algo como el patrón de modelo de lectura , las entidades no necesitan nada más que los atributos necesarios para el comportamiento.

si en lugar de tener Customer.Address (de tipo string) creamos una propiedad Customer.Address de type Address, ¿movimos el atributo de la dirección a un objeto VO asociado (que es de tipo Address) o diríamos que el Cliente todavía contiene la dirección ¿atributo?

Sí, en efecto, no hay diferencia entre una dirección de cadena o una dirección Address VO.

El autor no está equivocado aquí, ya que si asumimos que cada uno de los muchos números de teléfono de contacto que el Cliente tiene solo pertenece a ese Cliente en particular, entonces diría que estos números de teléfono están asociados con la identidad tanto como cuando el Cliente solo tenía un numero de telefono?

No estoy 100% en la intención del autor, pero creo que solo está describiendo cómo los requisitos de búsqueda de la entidad pueden alterar cómo la entidad y sus VO correspondientes son estructuras.

Fuera de tema, pero pensé que el modelo de dominio anémico resulta de mover el comportamiento fuera de una entidad, mientras que su ejemplo está llenando una entidad con muchos atributos, lo que resultaría en que el Cliente tenga demasiado comportamiento (ya que probablemente también incluiríamos en el Cliente el comportamientos que modifican estos atributos adicionales) y, por lo tanto, en violación de SRP?

Muchos atributos no implican mucho comportamiento. De hecho, generalmente sugiere lo contrario. Muchos atributos con captadores y establecedores, pero sin comportamiento de encapsulación.


Hice una actualización
EdvRusj,

1

TL; DR: has terminado de pensar esto. Sin embargo, me divertí pensando demasiado contigo. Así que abróchate el cinturón ...

La responsabilidad única (razón para cambiar) de una entidad debe ser identificarse de manera única, en otras palabras, su responsabilidad debe ser localizable.

No, eso no está bien. La responsabilidad única de una entidad es la continuidad.

La identidad es una consecuencia emergente de la continuidad. Modelar la identidad como una idea separable es una optimización del rendimiento.

Aquí hay un ejemplo: un cliente del restaurante le da un auto al valet. Una hora después, un patrón del restaurante pregunta por el auto. ¿Debería darlo el valet?

Es fácil decir que el valet debería darle el auto si el patrón es el "mismo". Pero, ¿qué significa esto realmente? La manera correcta de determinar eso es comenzar con el patrón "ahora" y buscar hacia atrás a través de la historia de ese patrón para ver si la entrega del auto al valet es parte de esa historia.

En realidad no podemos hacer eso, por supuesto. Tenemos problemas para rastrear nuestra propia historia con precisión, no importa la historia de algo que no estuvo con nosotros todo el tiempo. Entonces, en lugar de usar la historia del patrón, tomamos atajos. ¿El usuario posee el talón de boleto que tiene el mismo número que la etiqueta actualmente atada a las llaves? ¿La licencia de conducir en la billetera del usuario coincide con el nombre del título en el DMV, la imagen de la licencia de conducir se parece a la cara del usuario? Etc.

En resumen: en lugar de verificar el historial del usuario, verificamos el estado actual del usuario para ver si el estado actual es consistente con un historial que abarca el período entre la llegada del automóvil y la solicitud de devolución.

Cuando modelamos entidades, usamos una optimización análoga. Otorgamos a todas las entidades las responsabilidades comunes de

  1. Asegurar que el comienzo del historial incluya una asignación de un identificador inmutable al estado del objeto
  2. Asegurarse de que el siguiente estado siempre incluya una copia fiel del identificador del estado anterior.

No estoy describiendo una segunda responsabilidad de la entidad aquí; la entidad sigue siendo responsable de la continuidad, asegurándose de que la historia sea una narración coherente. Las responsabilidades del identificador son solo un subconjunto que son comunes a todas las entidades.

Todavía no tenemos ninguna aplicación de singularidad. Eso no es posible dentro de una sola entidad, porque la unicidad requiere acceso al estado de todas las entidades; donde una sola entidad solo tiene acceso a la suya.

Una vez más, verificar todos los identificadores cada vez no es práctico, por lo que satisfacemos la singularidad de la manera fácil: el código que genera el siguiente identificador nunca debe repetirse.

Al final, esto significa que podemos verificar la continuidad al probar la equivalencia de dos piezas de estado diferentes en la memoria, ahorrando mucha molestia al tratar de consultar gráficos acíclicos.

También parece haber confundido el Principio de responsabilidad única (que es una muy buena idea) con un principio de responsabilidad atómica. Descomponer una responsabilidad en partes más pequeñas y más fáciles de manejar es compatible con SRP.


-3

Bueno, depende de cómo quieras verlo.

Otra forma es: "¿El principio de responsabilidad única está violando la entidad del dominio?"

Ambas son pautas. No existe un "principio" en ninguna parte del diseño de software. Sin embargo, hay buenos diseños y malos diseños. Ambos conceptos se pueden usar de diferentes maneras para lograr un buen diseño.


Votos a favor inexplicables == SRP fanboys
h bob
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.