¿Cómo combinar estrictos TDD y DDD?

15

TDD se trata de diseñar código, guiado por pruebas.
Por lo tanto, las capas típicas no suelen construirse por adelantado; deberían aparecer ligeramente a través de pasos de refactorización.

El diseño impulsado por el dominio implica muchos patrones técnicos, que definen capas bien establecidas como la capa de aplicación, la capa de infraestructura, la capa de dominio y la capa de persistencia.

Para comenzar la parte de codificación de un proyecto DDD desde cero, ¿cómo comportarse?
¿Debo dejar estrictamente que el diseño surja de las pruebas, lo que significa que no hay separación de preocupaciones (sin capas) y refactorización para que se ajusten a los patrones técnicos DDD?

¿O debería crear esas capas vacías (aplicación, entidades / servicios de dominio, infraestructura) y dejar que TDD encaje en cada una de ellas de forma independiente (usando simulacros para aislar entre capas)?

Mik378
fuente
3
Relacionado: ¿TDD conduce al buen diseño?
Doc Brown

Respuestas:

12

Asegúrese de revisar los comentarios recientes del tío Bob sobre el papel del diseño en TDD .

El diseño impulsado por el dominio implica muchos patrones técnicos, que definen capas bien establecidas como la capa de aplicación, la capa de infraestructura, la capa de dominio y la capa de persistencia.

Udi Dahan: "Dios, cómo odio las capas". Pasa algo de tiempo discutiéndolo en su charla CQRS, pero diferente (la estratificación comienza a las 18m30s)

Deletrearía tu oración de manera ligeramente diferente; "DDD reconoce que hay una serie de preocupaciones comunes a la mayoría de las aplicaciones comerciales y que las soluciones a esas preocupaciones tienen vidas diferentes" .

Por ejemplo, las preocupaciones de dominio, por regla general, deben ser flexibles, especialmente cuando está personalizando una solución para un negocio en particular. Después de todo, el dominio se refiere a cómo la empresa hace negocios, es decir, cómo la empresa gana dinero y poder ofrecer mejoras comerciales rápidamente es un ingreso gratuito.

Por otro lado, probablemente no necesite cambiar el componente de persistencia con frecuencia. La solución de base de datos que funcionó la última versión probablemente también funcionará en esta versión.

Las preocupaciones de la aplicación están en algún punto intermedio; tienden a ser estables para que los usuarios no necesiten aprender una nueva aplicación con cada lanzamiento.

Además, puede haber múltiples implementaciones para resolver cualquier problema. Por ejemplo, la aplicación puede necesitar solo una instantánea de su estado actual; basta con guardar un archivo en el disco. Y en sus primeras iteraciones, eso puede ser todo lo que el dominio necesita también. Pero eventualmente llega una historia que requiere soporte de consultas ad-hoc, y usted reconoce que configurar una base de datos relacional será mucho más fácil que implementar una desde cero. Y luego está esta característica que funcionaría mejor en una base de datos gráfica.

Mientras tanto, el CTO quiere una versión de la aplicación que se ejecute en su teléfono; el CEO acaba de escuchar de un tipo que publicar una API es lo más importante.

Además, el equipo de ventas utiliza un modelo diferente, por lo tanto, dénos la misma aplicación, con un modelo diferente. Oh, pero estamos viajando mucho, por lo que nuestra versión debe funcionar cuando estamos desconectados y sincronizarnos más tarde ...

En otras palabras, aplica los patrones tácticos de no implementando marcadores de posición vacíos y suponiendo que se completarán más tarde, sino que al reconocer cuándo está cruzando las corrientes "Oye, ese es el código de persistencia en mi modelo de dominio, no debo ser hecho refactorización todavía ".

VoiceOfUnreason
fuente
11

Test Driven Development (TDD) no es un diseño. Es un requisito que impacta su diseño. Al igual que si fuera necesario ser seguro para subprocesos, eso no es un diseño. Nuevamente, es un requisito que impacta su diseño.

Si ignora alegremente todas las demás preocupaciones de diseño y respeta religiosamente las reglas de TDD, no culpe a TDD cuando su código se convierte en basura. Será basura comprobable pero será basura.

Una cosa buena de la basura comprobable es que es una basura refactible, por lo que para algunas personas es lo suficientemente buena. Nos pondremos elegantes solo cuando sea necesario. Otros odian esto y culpan a TDD por ello. No. Esto es cosa tuya.

El diseño controlado por dominio (DDD) es algo que hace antes del ciclo de refactorización verde rojo de TDD.

DDD es el esfuerzo por crear y preservar un espacio en el código donde un experto en dominios, que en gran medida ignora los detalles del sistema, puede entender cómo controlar el sistema. Esto se realiza mediante abstracción y modelado de un dominio problemático de una manera familiar.

Un sistema DDD puede tener una arquitectura similar a esta:

ingrese la descripción de la imagen aquí

Esta arquitectura DDD tiene muchos nombres: Clean , Onion , Hexagonal , etc.

Aquí está la desconexión que veo que muchas personas tienen cuando miran este diseño. Esto no es concreto. Puedo seguir este diseño y nunca he escrito nada de lo que ves diagramado aquí. Veo que otros insisten en que debe haber un objeto de caso de uso o una clase de entidades. Estas son un conjunto de reglas que le indican con quién puede hablar y cómo.

Eso es. Siga las reglas de este diseño y puede TDD su pequeño corazón. A TDD no le importa con quién hables. Le importa que todo lo que hace algo pueda demostrarse que funciona o no con solo hacer clic en un botón. No, algo en alguna parte está roto. Te dice exactamente lo que está roto.

¿Todavía vago? Mire el diagrama Controler- Use Case Interactor- Presenteren la esquina inferior derecha. Aquí hay tres cosas concretas que se comunican entre sí. Claro que esto es DDD, pero ¿cómo agrega TDD aquí? Solo burlarse de las cosas concretas. El presentador debe estar recibiendo información. Una PresenterMockclase sería una buena manera de comprobar que está obteniendo lo que esperaba. Repartir la Use Case Interactordel PresenterMocke impulsar el Use Case Interactorcomo si fuera el Controllery tienes una buena manera de probar la unidad del Use Case Interactorpuesto que la maqueta le dirá si tiene lo que se espera que va a obtener.

Pues mira eso. TDD satisfecho y no tuvimos que seguir con nuestro diseño DDD. ¿Cómo pasó eso? Comenzamos con un diseño bien desacoplado.

Si usa TDD para impulsar el diseño (no simplemente D evelopment), obtiene un diseño que refleja el esfuerzo que le dedicó. Si eso es lo que quieres bien. Pero eso nunca fue para lo que estaba destinado TDD. Lo que esto termina por carecer ciertamente no es culpa de TDD.

TDD no se trata de diseño. Si tiene que hacer cambios de diseño para usar TDD, tiene mayores problemas que las pruebas.

naranja confitada
fuente
Nunca dije que TDD es un diseño, pero se trata de diseño.
Mik378
1
Tío Bob te estaba diciendo que diseñaras. Él no te decía "oye, si tu trabajo de prueba se preocupa por el resto".
candied_orange
1
Como ya dije, solo sigue las reglas de con quién puedes hablar. La arquitectura limpia no es algo para debatir sobre BDUF. Simplemente identifique en qué parte se encuentra y piense en quién y cómo debe comunicarse. Es más ágil de lo que piensas. Después de eso, pregunte si TDD puede comprobarlo. Si no es así, hiciste algo mal. Si es así, espero que haya prestado atención porque un buen diseño es más que solo comprobable.
candied_orange
66
Ugh ... Realmente no puedo soportar el nombre inapropiado de "Test Driven Design". Todavía tiene que diseñar un poco antes de comenzar a golpear felizmente su teclado, ya sea que escriba o no pruebas.
RubberDuck
1
Para citar al tío Bob sobre este punto exacto, "Tienes que DISEÑAR el período" . Haga clic allí y, si está demasiado impaciente para leer todo, busque esas palabras. Descubrirá que Martin es bastante firme en que TDD no es una bala mágica y que debe diseñar no solo su código sino también sus pruebas si no quiere vivir en una base de código muy frágil.
candied_orange
4

TDD asegura que su código tiene todos los casos de prueba necesarios escritos en paralelo al desarrollo. Esto no debería afectar el diseño de alto nivel. Piénselo más en el trabajo de las trincheras.

DDD tiene que ver con diseños de alto nivel, lenguaje entre expertos e ingenieros de dominio, mapeo de contexto, etc. Este debería ser el impulsor del diseño de alto nivel de la aplicación.

Ambas son explicaciones superficiales de dos poderosas metodologías de programación. Pero al final del día realmente logran dos cosas muy diferentes.

Comience con el lenguaje DDD y el mapeo de contexto y luego, cuando vaya a escribir el código, comience la práctica de TDD. Pero la práctica de TDD no debería afectar el diseño de alto nivel, pero debería asegurar que las cosas se puedan probar. Hay una pequeña advertencia aquí.

Creo que puede ser importante tener en cuenta: solo debe practicar DDD si la aplicación es lo suficientemente compleja.

Matt Oaxaca
fuente
1
No estoy de acuerdo, TDD no se trata de pruebas, se trata de diseñar.
Mik378
Estoy basando todo en las 3 reglas de TDD como lo describe el tío Bob.
Matt Oaxaca
Steve Freeman, autor del libro GOOS declaró: no debe especificar ninguna capa o infraestructura antes de comenzar los ciclos TDD.
Mik378
No estoy familiarizado con ese libro, pero tendría que estar en desacuerdo. No quiero que TDD forme mi gráfico DI y clase.
Matt Oaxaca
3
@ Mik378: TDD no se trata de pruebas, pero tampoco se trata principalmente de diseñar: el único diseño inducido por TDD es el diseño de la capacidad de prueba de la unidad. Cualquier otra parte del diseño debe provenir de otro lugar. Veo TDD más como una técnica de implementación.
Doc Brown
3

DDD es sobre diseño de software.
TDD es sobre diseño de código.

En DDD, el "modelo" representa la abstracción del dominio, todo el conocimiento del dominio experto.

Podríamos usar TDD para el modelo de diseño de software de código inicial. El dominio tiene reglas de negocio y modelos de dominio que la prueba escrita (primero) debe ser verde.

En efecto, podemos codificar las pruebas, después de diseñar un modelo controlado por dominio.
Este libro "Growing Object-Oriented Software, Guided by Tests" link-for-buy
Tome este enfoque, con un esqueleto ambulante , arquitectura hexagonal y TDD.

Fuente de: DDD rápidamente - InfoQ

JonyLoscal
fuente
1
bueno para mí, el software y el código son lo mismo
Konrad
1
También podría ser lo mismo. Traté de decir: software como "solución", "sistema", "alto nivel" y código como "implementación", "bajo nivel", "detalles".
JonyLoscal
Creo que lo importante es que "primero probamos, pero necesitamos un esqueleto mínimo donde comenzaríamos a probar". ¿Vos si?
JonyLoscal
1

¿Debería dejar estrictamente que el diseño surja de las pruebas?

No. (impulsado por el dominio) El diseño por definición debe surgir de los requisitos del dominio. Es una mala idea dejar que cualquier otra cosa conduzca su diseño, ya sea un conjunto de pruebas, un esquema de base de datos o ... (continuará)

¿O debería crear esas capas vacías (aplicación, entidades / servicios de dominio, infraestructura) y dejar que TDD encaje en cada una de ellas de forma independiente

(continuación) ... o algunas capas canónicas de clases / jerarquías de clases en su idioma favorito de OO, incluso si es muy maduro y popular (después de todo, "millones de moscas no pueden estar equivocadas", ¿verdad?) .

Cuando se trata de DDD, OOP no cumple con los requisitos de expresión en forma legible para humanos, es decir, algo que sería más o menos claro para un no programador. Los lenguajes FP estrictamente escritos hacen un mejor trabajo. Recomiendo leer un libro sobre DDD usando la programación funcional "Modelado de dominio hecho funcional" por Scott Wlaschin

https://pragprog.com/book/swdddf/domain-modeling-made-functional

No tiene que usar el lenguaje FP para tomar prestadas algunas de las ideas de allí (no todas desafortunadamente), pero si realmente lo lee, entonces probablemente quiera usar un lenguaje funcional.

También responderá a su pregunta sobre cómo encaja TDD en la imagen DDD. En pocas palabras, cuando los requisitos están codificados en un estilo funcional, elimina la necesidad de una gran cantidad de pruebas unitarias, ya que hace que la mayoría de los estados y escenarios no válidos sean irrepresentables / imposibles de compilar. Por supuesto, todavía hay lugar para las pruebas automatizadas en el proyecto FP, pero de ninguna manera las pruebas impulsarán las decisiones de diseño más importantes.

Para hacer un círculo completo volvamos a la pregunta del título, es decir, "¿Cómo combinar TDD estricto y DDD?". La respuesta es sencilla: no hay nada que combinar / no hay conflicto de intereses. Diseñe de acuerdo con los requisitos, desarrolle de acuerdo con el diseño (escribiendo pruebas primero si realmente desea hacer TDD)

Kola
fuente
+1 para el libro FP DDD y explicación general.
Roman Susi