Estamos tratando de mover datos de nuestra capa de Servicio hinchada a nuestra capa de Dominio usando un enfoque DDD. Actualmente tenemos mucha lógica de negocios en nuestros servicios, que se extiende por todo el lugar y no se beneficia de la herencia.
Tenemos una clase de dominio central que es el foco de la mayor parte de nuestro trabajo: un comercio. El objeto Trade sabrá cómo valorarse a sí mismo, cómo estimar el riesgo, validarse a sí mismo, etc. Luego, podemos reemplazar los condicionales con polimorfismo. Por ejemplo: SimpleTrade se cotizará de una manera, pero ComplexTrade se cotizará de otra manera.
Sin embargo, nos preocupa que esto hinche las clases de Comercio. Realmente debería estar a cargo de su propio procesamiento, pero el tamaño de la clase aumentará exponencialmente a medida que se agreguen más funciones.
Entonces tenemos opciones:
- Ponga la lógica de procesamiento en la clase Trade. La lógica de procesamiento ahora es polimórfica según el tipo de operación, pero la clase de operación ahora tiene múltiples responsabilidades (fijación de precios, riesgo, etc.) y es grande
- Ponga la lógica de procesamiento en otra clase, como TradePricingService. Ya no es polimórfico con el árbol de herencia Trade, pero las clases son más pequeñas y más fáciles de probar.
¿Cuál sería el enfoque sugerido?
Respuestas:
Si va a ir impulsado por el dominio, considere tratar su clase Trade como una raíz agregada y divida sus responsabilidades en otras clases.
No desea terminar con una subclase de Comercio para cada combinación de precio y riesgo, por lo que un Comercio puede contener objetos de Precio y Riesgo (composición). Los objetos Precio y Riesgo hacen los cálculos reales, pero no son visibles para ninguna clase, excepto Comercio. Puedes reducir el tamaño del Comercio, sin exponer tus nuevas clases al mundo exterior.
Trate de usar la composición para evitar grandes árboles de herencia. Demasiada herencia puede conducir a situaciones en las que intentas calzar un comportamiento que realmente no se ajusta al modelo. Es mejor llevar esas responsabilidades a una nueva clase.
fuente
Su pregunta definitivamente me hace pensar en el Patrón de Estrategia . Luego puede intercambiar varias estrategias de negociación / fijación de precios, similares a lo que está llamando a
TradePricingService
.Definitivamente creo que el consejo que obtendrá aquí es usar composición en lugar de herencia.
fuente
Una posible solución que he usado en un caso similar es el patrón de diseño del adaptador (la página referenciada contiene muchos códigos de muestra). Posiblemente en combinación con el patrón de diseño de delegación para facilitar el acceso a los métodos principales.
Básicamente, divide la funcionalidad del comerciante en varias áreas disjuntas, por ejemplo, manejo de precios, riesgos, validación, todas podrían ser áreas diferentes. Para cada área, puede implementar una jerarquía de clases separada que maneje esa funcionalidad exacta en las diferentes variantes necesarias, todas una interfaz común para cada área. La clase principal de Trader se reduce a los datos más básicos y referencias a una serie de objetos de controlador, que se pueden construir cuando sea necesario. Me gusta
Una ventaja importante de este enfoque es que las posibles combinaciones de, por ejemplo, precios y ricks están completamente separadas y, por lo tanto, se pueden combinar según sea necesario. Esto es bastante difícil con la herencia de un solo subproceso de la mayoría de los lenguajes de programación. La decisión de qué combinación usar puede incluso calcularse muy tarde :-)
Por lo general, trato de mantener las clases de adaptador, por ejemplo, las subclases de
IPriceCalculator
arriba, sin estado. Es decir, estas clases no deberían contener ningún dato local si es posible, para reducir el número de instancias que deben crearse. Por lo general, proporciono el objeto principal adaptado como argumento en todos los métodos, como en elgetPrice(ITrader)
anterior.fuente
no puedo decir mucho sobre tu dominio, pero
... es un olor para mí. Probablemente intente esbozar las diferentes responsabilidades de la clase y eventualmente descomponerlo en diferentes agregados. Los agregados se diseñarían en torno a roles y / o puntos de vista de los interesados / expertos en el dominio involucrados. Si Price y Risk están involucrados en el mismo comportamiento / caso de uso, probablemente pertenecerían al mismo agregado. Pero si están desacoplados, pueden pertenecer a agregados separados.
Tal vez RiskEvaluation podría ser una entidad separada en su dominio, eventualmente con un ciclo de vida específico (realmente no puedo decir, simplemente estoy especulando ... usted conoce su dominio, no lo sé), pero la clave es hacer conceptos implícitos explícito y para evitar el acoplamiento que no está impulsado por el comportamiento, sino solo por el acoplamiento de datos heredados.
En general, pensaría en el comportamiento esperado y los diferentes ciclos de vida de los componentes involucrados. Solo agregar comportamiento sobre los datos agrupados crea objetos hinchados. Pero los datos se han agrupado de acuerdo con un diseño basado en datos existente, por lo que no hay necesidad de atenerse a eso.
fuente