Uso de anti-requisitos para encontrar los límites del sistema

A todos nos encanta construir proyectos greenfield. 1 Pero, inevitablemente, comenzar un nuevo proyecto implica muchas reuniones con las partes interesadas del negocio para analizar los requisitos iniciales y los modelos de datos canónicos. Esos son… no tan divertidos.

Cuando una de esas reuniones ocurre después de un almuerzo rico en carbohidratos, es fácil que su mente se desvíe… de regreso a esas conferencias universitarias sobre el diseño de entidades. Piensa en los sustantivos y qué atributos tienen. Un perro y un gato son animales y tienen 4 patas. Excepto que ahora son Clientes, Pedidos, Productos y Carritos de compras.

Sin embargo, ¿es esta la mejor manera de construir un sistema? ¿No hicimos exactamente lo mismo en el proyecto greenfield anterior que ahora estamos reescribiendo? Seguramente no cometeremos los mismos errores que cometimos la última vez… ¿verdad?

🔗Un carro para gobernarlos a todos

A medida que continúa la reunión, el diseño del carrito de compras comienza a tomar forma. ShoppingCart es un sustantivo, después de todo, y tiene una lista de elementos, cada uno de los cuales tiene atributos simples como Price y Quantity. Aquí está la parte del carrito de compras del diagrama entidad-relación que imprimiremos y mantendremos en nuestro escritorio 2 como un artículo sagrado de las escrituras de diseño de software:

Primera versión del carrito.

También nos hemos dado cuenta de que un carrito tiene algún comportamiento asociado, operaciones como AddToCart(), SaveForLater()y Checkout(). Así que ahora estamos combinando datos y comportamiento juntos… esto es esencialmente un agregar ¡lo que significa que ahora estamos haciendo un diseño basado en dominios!

🔗Más atributos, más problemas

Durante el desarrollo, comenzamos a ver algunas fallas en el plan.

Primero, aprendemos que si el precio de un artículo baja, el nuevo precio más bajo también debería reflejarse en el carrito de compras. Entonces, cada vez que cambia un precio, debemos copiar ese valor en cualquier carrito de compras que contenga ese artículo. Sin embargo, si el precio de un artículo sube, debemos advertirlo al usuario y hacerle aceptar el nuevo precio. Así que ahora los artículos del carrito necesitan almacenar el precio actual y el precio anterior, y tenemos que hacer muchas copias cada vez que cambia cualquier dato relacionado.

A continuación, nos damos cuenta de que necesitamos que el nivel de inventario refleje con precisión el inventario disponible en el almacén. La empresa tiene la intención de usar esto para presionar a los clientes a comprar antes de que desaparezca. 3

Para mantener este valor actualizado, cada vez que cambie el inventario de cualquier artículo en el almacén, tendríamos que verificar cada carrito de compras activo en busca de una instancia de ese artículo y actualizar su valor. Es posible que pueda unirse a tablas para obtener esta información, pero esa no siempre es una opción. Por ejemplo, es posible que necesite que los datos se desnormalicen para el rendimiento, o que los datos del almacén existan en un sistema físicamente diferente que no puede participar en una unión de base de datos.

Se pone peor. Resulta que descubrimos preocupaciones similares sobre las estimaciones de entrega, los nombres de los artículos y las descripciones. Por lo tanto, cada vez que alguno de estos valores cambie, también deberán copiarse de su fuente de verdad a cualquier carrito de compras con un artículo coincidente. Al menos la gente de marketing insiste en que los cambios en los nombres y descripciones de los productos deben ser poco frecuentes y limitarse principalmente a errores tipográficos. Esperemos que eso sea cierto.

Así que ahora nuestro carrito de compras comienza a verse mucho más desordenado, y empezamos a preocuparnos pensando en todo el trabajos por lotes necesitaremos escribir para mantener esto actualizado.

El carrito de compras se ha vuelto complejo

Nuestro objeto Cart ya no se ve como un agregado DDD adecuado, con todo dependiente de todo lo demás y los datos se copian en todas partes.

La sensación de hundimiento de déjà vu del viejo proyecto comienza a aparecer. ¿Qué pasó? Y, lo que es más importante, ¿cómo podemos solucionarlo?

🔗Antirrequisitos al rescate

Para ayudar a descomponer un dominio complejo, podemos usar anti-requisitos 4 para encontrar atributos incorrectamente agrupados en la misma entidad. El uso de anti-requisitos es una forma poderosa de aumentar la autonomía al dividir su dominio en islas separadas que pueden evolucionar de forma independiente. 5

Los anti-requisitos son engañosamente simples: crea un requisito falso relacionado con dos atributos y lo presenta a las partes interesadas del negocio. “Si el producto tiene más de 20 caracteres en su nombre”, les dices, “entonces su precio debe ser de al menos $20”.

Cuando se ríen de ti, es un indicio de que aunque esos dos atributos están asociados verbalmente con el mismo sustantivo, no existe una relación lógica significativa entre ellos. 6

Sin anti-requisitos, desentrañar estos detalles puede ser complicado. Dado que los expertos en dominios comerciales tienden a pensar en estas cosas como obvio, lo que hace que sea poco probable que ofrezcan voluntariamente esta información. Por lo general, se sorprenden de que los desarrolladores aún no lo sepan. Eso hace que nuestro trabajo como desarrolladores y arquitectos sea buscarlo.

Entonces, con esto en mente, volvamos a nuestro carrito de compras y preguntémonos: ¿La gente de negocios pensará que me he perdido si pregunto qué reglas comerciales podrían operar en el Atributo A y el Atributo B? Si la respuesta es sí, es probable que haya encontrado un requisito contrario.

🔗Un carro nuevo y mejorado.

Empecemos a desmenuzar algunos anti-requisitos y veamos qué efecto tiene en nuestro carrito de compras, comenzando con el concepto de precio.

  • Cuando el precio de un producto supera los $100, el nombre debe cambiarse a mayúsculas. ¡Ridículo!
  • Cuando la descripción de un producto tiene más de 3000 caracteres, el precio debe incrementarse en un 10%. ¡Ridículo!
  • Cuando el inventario de un artículo es superior a 1000, debemos cobrar un 10% más. ¡Inconcebible!

Pero espera, tenemos que tener cuidado. Al escuchar ese último anti-requisito, nuestra parte interesada comercial podría decir que si bien eso es realmente inconcebible, podría Es posible que tengamos que cobrar más cuando el inventario es bajo. Después de todo, eso es solo oferta y demanda en acción. Al utilizar los requisitos antirrequisitos de esta manera, es posible que descubra accidentalmente requisitos comerciales que de otro modo podrían haberse pasado por alto.

Pero sean cuales sean los anti-requisitos que soñemos, queda claro que el precio y la cantidad están relacionados. Después de todo, debes multiplicar price × quantity para obtener el costo total.

Esto sugiere que los valores de precio y cantidad altamente acoplados podrían extraerse de otra parte.

Precio y cantidad extraída a Ventas

De la misma manera, podemos comenzar a analizar otros pares de atributos, elaborando anti-requisitos para cada uno y usando lo ridículos que suenan para determinar si extraer otros grupos de atributos que están más estrechamente acoplados.

  • El nombre de un producto afecta la entrega estimada porque enviamos los productos en orden alfabético. ¡Absurdo!
  • Debemos actualizar la descripción de un artículo cada vez que cambia el nivel de inventario. ¡Absurdo!
  • Cuanto más inventario tengamos de un artículo, más tiempo llevará enviarlo. Wackadoodle! 7 8

Vista final del carro de la compra en múltiples servicios

¿Recuerdas esa entidad del carrito de compras? Usamos los anti-requisitos como garrote para hacerlo pedazos. Resulta que, si bien un carrito de compras es un sustantivo utilizado por el negocio, ya no existe un “carrito”… solo un simple CartId en lugar de una entidad o agregado en toda regla.

Los lectores con ojo de águila notarán aquí que el Quantity no es propiedad de ninguna cosa, sino que se comparte entre Ventas, Envíos y Almacén. Es importante darse cuenta de que incluso los atributos únicos no siempre significan lo mismo. En Ventas, la cantidad es un multiplicador del precio. En Envío, es cuántos artículos poner en una caja… o incluso varias cajas. En Warehouse, se trata de cuántas cosas reservar y reabastecer. Los valores simplemente provienen del mismo lugar, y le mostraremos cómo manejar eso un poco más adelante.

Esto muestra que no todos los sustantivos que usa la empresa necesitan tener una entidad correspondiente en su modelo de dominio.

🔗Eficiencia mejorada

Solo agrupar datos que cambian juntos también tiene muchas ventajas técnicas y organizativas.

Desde una perspectiva técnica, los atributos que cambian juntos también deben almacenarse en caché de manera similar. Por ejemplo, el nombre y la descripción de un producto no cambian con frecuencia y se pueden almacenar en caché durante mucho tiempo, pero el precio y el inventario pueden cambiar con frecuencia. Almacenarlos en diferentes entidades nos permite utilizar la estrategia de almacenamiento en caché más adecuada para cada uno. En nuestro caso, almacenar el nombre y la descripción del producto en un archivo JSON alojado en una red de entrega de contenido (CDN) podría ser un enfoque mejor y más escalable que usar una base de datos relacional.

De hecho, si está almacenando imágenes de productos en un CDN según una convención como https://mycdn.com/products/{ProductID}/{Size}.pngentonces ya ha comenzado a descomponer su dominio utilizando estas estrategias.

Desde una perspectiva organizacional, ya no necesita reunir a todas las partes interesadas del negocio simultáneamente. 9 Si necesita agregar la capacidad de entregar productos digitales que no requieren envío, la cantidad de personas que deben participar ahora se reduce significativamente a solo las personas con conocimientos sobre las partes relevantes del “carrito”.

El único problema que le queda a nuestro carrito de compras es tomar todas las piezas de Humpty Dumpty y volver a armarlas.

🔗Composición de ViewModel

Todo tiene un costo, y descomponer usando anti-requisitos no es diferente. Cada componente gana mayor autonomía, pero se puede sentir (al principio) que esto tiene un precio de mayor complejidad.

Nuestros usuarios todavía piensan en un “carrito de compras” como una cosa y esperan ver todos los atributos que hemos separado en una página de carrito de compras juntos.

Podemos integrar todos los atributos del carrito de compras en la misma página usando una estrategia llamada Composición de ViewModel utilizando herramientas como Compositor de servicios donde los componentes independientes proporcionados por servicios o microservicios 10 pueden consultar sus propios datos de sistemas back-end dispares y combinarlos en un solo ViewModel sin volver a introducir el acoplamiento en la capa de la interfaz de usuario.

En la composición de ViewModel, cada componente registra su interés en proporcionar datos para patrones de ruta de URI específicos. Luego, para cada solicitud web, se solicita a todos los proveedores de datos interesados ​​que obtengan sus datos, que se agregan a un ViewModel dinámico. Finalmente, un servicio separado (llamémoslo Branding) toma el ViewModel y lo representa en HTML.

La historia es similar para las solicitudes POST. Los componentes registran controladores para rutas POST para comunicarse con sus respectivos sistemas back-end, generalmente con mensajes asíncronos. Así es como cada servicio persiste el Quantity valor de vuelta a su propio almacén de datos sin ningún conocimiento de los otros componentes.

El uso de la composición de ViewModel agrega algo de complejidad, al menos a corto plazo. No hará que la construcción de la primera pantalla sea más rápida, pero voluntad haga que la construcción de la pantalla 10, 50 y 100 sea más rápida. Limitar el acoplamiento innecesario, incluso en la capa de la interfaz de usuario, facilita continuar creando nuevas funciones en el futuro.

Las técnicas de composición de ViewModel también permiten flexibilidad en términos de tecnología de almacenamiento de datos. Por ejemplo, un servicio podría funcionar con una base de datos relacional tradicional. Al mismo tiempo, otro podría usar una base de datos de gráficos o un almacén de valores clave, una capa de almacenamiento en caché pesado con Redis o incluso archivos JSON en un CDN.

Y cuando un único servicio sólo tiene que preocuparse de los suyos corte vertical 11 del sistema general, encontrará que un diagrama de base de datos realmente puede cabe cómodamente en una sola hoja de papel.

Hay mucho más que decir sobre la composición de ViewModel de lo que se puede cubrir en una sola publicación de blog. Echa un vistazo a Mauro Servienti serie de blogs sobre la composición de ViewModel o su seminario web Todos nuestros agregados están mal 12 aprender más.

🔗Resumen

En los sistemas de software cada vez más complejos de hoy en día, el enfoque de modelado de “nombre tiene un atributo” está destinado a dar como resultado clases, componentes y sistemas que se convierten en un lío de acoplamiento. Los anti-requisitos son una estrategia que podemos usar para encontrar nuestros límites lógicos de servicioayudándonos a descubrir qué atributos van juntos y cuáles no tienen por qué estar cerca unos de otros.

Con el tiempo, demasiado acoplamiento hace que el sistema se convierta en una gran bola de lodo. Eventualmente, hacer cambios en cualquier lugar sin romper algo aparentemente no relacionado se vuelve imposible.

Las organizaciones que se desacoplen en servicios autónomos podrán ser más ágiles y ofrecer valor a los años comerciales en el futuro. Después de todo, a diferencia de la mayoría de los otros proyectos empresariales, el software nunca está realmente “terminado”.

Todos los demás estarán atrapados reescribiendo el sistema en 3 años. De nuevo.

Leer Más