#DDD - Dudas conceptuales
1 messages · Page 1 of 1 (latest)
La primer duda que tengo es, cuando vale la pena guardar entidades dentro de otra entidad (embebidas).
** Sistema de Inventario**
1. Requerimientos
-REQ-1: Se debe llevar un registro de a) articulos disponibles, b) artículos reservados, c) artículos entregados
-REQ-2: Se debe notificar al administrador cuando un artículo no tenga unidades disponibles
Entidades
- Stock: colección de artículos
- Asset: artículos
Mi duda viene a raíz de REQ-2 las notificaciones, en DDD deberían ser un Evento de Dominio, en este caso, podría ser StockLevelChanged, y en pseudocodigo, bastaría hacer
StockLevelChanged event; // evento de cuando cambia el stock
if (event.stock.getAvailableCount() === 0) {
// notificar a los interesados
}
Pero, ahora viene la parte divertida, cómo debo guardar las unidades disponibles en la entidad Stock ?
podría guardar una lista de Assets para cada tipo, una para disponibles, otro para reservados, y una para entregados
class Stock {
private Asset available[];
private Asset reserved[];
private Asset delivered[];
public int getAvailableCount() {
return available.length;
}
}
Pero, al hacer esto, estaría embebiendo la entidad Asset, por lo que al reservar, liberar o entregar artículos, tendría un cuello de botella, ya que si utilizamos concurrencia optimista (Optimistic-Locking), cada artículo bloquea todo el Stock
En su lugar, se me ocurre esto
class Asset {
public String stockId;
public int state; /*0 = available, 1 = reserved, ...*/
...
}
Y crear el evento AssetStateChanged, el cual Stock se subscribe
AssetStateChanged event = ...;
Asset asset = event.asset;
Stock stock = StockRepository.findByPk(asset.stockId);
int previous_state = event.previousState;
int current_state = asset.getState();
if (/*pasó a estar disponbile*/) {
stock.incrementAvailableCount();
} else if (/*pasó a estar reservado*/) {
stock.incrementReservedCount();
}
...
if (/*dejó de estar disponible*/) {
stock.decrementAvailableCount();
} else if (/*dejó de estar reservado*/) {
stock.decrementReservedCount();
}
...
Y finalmente para Stock
class Stock {
private int available_count;
private int reserved_count;
private int delivered_count;
public int getAvailableCount() {
return available_count;
}
}
Esto garantiza que los Asset se reserven, liberen, o entreguen sin bloquearse unos a otros, pero me parece raro una clase Stock que guarde puro entero jajaj, ¿Cómo lo harías tú?
@olive isle
Es que veo a muchos empotrar las entidades, o bien, los ids de las entidades que hacen referencia, pero esto a mí no me hace sentido, si la colección guarda demasiadas instancias de la entidad incrustada. Más que nada porque no se podrían editar sin bloquearse unas de otras. Alguna vez te has topado con un caso así?
Aclaro, este caso es ficticio. Solo pensé que era el más simplista al exponer la problemática que me suelo encontrar al día a día. Una entidad A, que guarda referencias o empotra directamente la entidad B, no obstante, se desea que se pueda modificar los objetos de B sin que bloqueen la entidad que los colecciona (A), o bien, se bloqueen unos a otros.
Requerimientos a nivel codigo:
cls Asset [id, name, ...stuff]
cls Stock [id, asset_id, count, ...stuff]
table Asset [id, name, ...stuff]
table Stock [id, asset_id]
table StockAssetLevel [stock_id, level, count]
query StockAssetCount join Stock.id->StockAssetLevel.stock_id
Los requerimientos son muy vagos asi que hay muchas maneras de resolverlo, principalmente no se plantea ninguna regla de negocio referido a Stock, un Stock podria ser N o M Assets (Y ahi tendria mas sentido que el Stock no exista a nivel codigo y se resuelva todo en la DAL).
Hay un par de reglas para decidir cuando separar o encapsular (anidar) entidades, un Aggregate es una entidad de nivel superior la cual es la encargada de comunicarse con el BC (Bounded Context), por ejemplo en este caso al ser tan vago podrias tener un Stock Aggregate y que la entidad Articulos sea dependiente de Stock, no tiene otro caso de relacion fuera del Aggregate y por ende no tiene porque estar en un nivel superior, ese es un caso de como identificar si tendrias o no anidar entidades. Mucho cuidado con esto porque evidentemente al encapsular/anidad entidades no permitis al context comunicarse directamente con ellas.
Otro caso de generacion de entidades es que tan influyente es un evento en si en el dominio, si un evento es muy influyente (En este caso que el que se lleve la ultima unidad de un producto se genera un seed y si cumple cierta condicion el seed entonces el producto sea gratis, que cuando se quede sin productos pero aparte no tengan reservados ergo sea 0/0/x el Stock en general se genere un tracking de hace cuanto no hay productos en el stock) eso podria ser considerado una entidad, la entidad y/o aggregates son algo que tiene impacto en el dominio y puede interacturar con el resto del dominio en un espacio/tiempo diferente al que un evento (El evento es triggereado mientras las entidades no necesariamente tienen que triggerar/ser triggereadas algo)
Con el primer parrafo te resumo lo que pienso de Asset y Stock, los requerimientos del dominio no estan definidos realmente, si esos son simplemente los requerimientos no tenes porque tratar a Asset y Stock como aggregates y simplemente hacer un Aggregate Stock y que Asset sea anidada a Stock, Asset no interactura y tampoco le interesa el estado, Asset solo le interesa que Asset es, no si esta o no reservado/entregado/disponible, eso es de Stock (Y si ampliaras un poco mas los requerimientos podriamos hablar de Bulk y separar Assets por Bulks, ahi si tendria mas sentido separar Asset y triangular estos 3 Aggregates)
Un detalle, en el ultimo ejemplo de Stock estas devolviendo available_count, posiblemente esa sea un VO que solo le interesa a Stock porque interactua de x manera con Asset por lo tanto al dominio no le interesa eso (Posiblemente tendrias que hacer un VO y que sea StockAvailableCount)
En casos simplistas es donde menos sentido tiene el pensar en DDD
Vos tenes que pensar que el "empotrar" id es simplemente porque son de otro Aggregate/BC, y las entidades anidadaas es simplemente porque no requieren su existencia a nivel BC o no tienen un impacto fuera de ese aggregate, eso simplifica tu dominio pero complejiza a nivel producto a futuro si hay que desacoplarlas, por eso pienso que en este caso los requerimientos son muy vagos y dificil de analizar
Igual no hay contexto a nivel Presentation o Infra (DTOs y DAL), lo de los eventos esta aceptable, cuando creas un stock automaticamente tenes el conocimiento de que Asset es el Stock y solo se encarga por incrementar o no el numero de stock, despues de eso si hay que notificar lo cumplis con el evento simplemente accediendo al DAL, pero a nivel dominio no hay mucho que analizar, simplemente acopla el counting al Stock y triggereas el evento de StockReserved para ver si ya no hay unidades disponibles
Level solo es un valor de Stock y no es un VO en si, es un estado de Stock el cual tiene un count de Assets
Tampoco soy dogmatico
Por ejemplo a mas de uno le romperia el corazon mi respuesta
Pero es que el dogmatismo no lleva a nada bueno, muchos productos llenos de dogmatismo no escalan o son una mierda
Y es justamente por eso que no respeto las reglas al 100%
Pienso en DDD como un set de sugerencias para definir un producto y CA como marco conceptual para separar las implementaciones del producto
Los BC son necesarios pero muchas veces se definen por un set de estupideces mezcladas de DDD + CA que pierden el hilo de con que reglas y para que lo definen
La idea de DDD es solucionar problemas del software acoplado y poder comunicar el software a los clientes el resto del equipo, CA es simplemente una estructura de codigo y un set de patrones necesarios para llevarlo a cabo (Events, Repository/DAO dependiendo de que tan denso quieran hacer la implementacion, Adapter, DTO)
Yo discuto mucho del dogmatismo del DDD, pienso que es hermoso pero tambien pienso que lo piensan tan ligado a una implementacion en especifico lo cual justamente rompe el motivo de DDD, DDD esta hecho para poder Diseñar, comunicar y escalar un producto sin problemas, teniendo una guia de como estructurar, armar y escalar tu arquitectura, cuando ya le meten CQRS + EV + CA DE LLENO SIN SIQUIERA ANALIZAR OTRAS OPCIONES es cuando digo "Empeza denuevo"DDD por ejemplo fitea perfectamente con una arquitectura Actor Model, siendo cada actor un BC o Aggregate dependiendo de como sea un dominio, ahi cambiariamos CQRS por Messages y EV tambien por Messages, te sacas dos patrones al pedo por uno solo que encima fitea de 10 con el proposito, con Actor model tenes una separacion a nivel dominio igual que en CA, con Messages cumplis con lo mismo que CA UseCase hecho con CQRS + ED
Por eso me parece ilogico el ponerse a pensar instantaneamente en cosas como Eventos (ED) + CA, tu Actor puede ser simplemente un Aggregate/BC y no estamos implementando CA necesariamente (Entidades), sacamos los Events (ED) y por mas de que no me hablaste de CQRS segur que lo pensaste
DDD Habla obviamente de eventos, pero los eventos no necesariamente son Event Driven, como te los presentan en cualquier post de Medium, los eventos basicamente son side effects fuera del Service, si X cambio raiseas un Event (Message AM/Event ED) y el EventActor/EventQueue se encarga de efectuar el side effect
Se lo mande al DM pero podria haberlo mandado aca igual
Glosario:
Aggregates: Top level entity en CA, puede referenciar a otros aggregates por su ID y tener entidades anidadas dependiendo del nivel de relacion con la entity
Entity: Entidad comun la cual se encarga de su estado y unicamente de su estado, puede referenciar unicamente a Aggregates pero eso seria por medio de un DomainService
VO/Value Object: Primitivos del dominio
Application/DomainServices: Los use case de toda la vida, unos son implementados a nivel application y otros a nivel dominio
DTOs: Los JSON que se devuelven en los controllers
DAL: Data access layer, donde se concentra adaptadores/repository impls
Controllers: Lo que comen los RestAPI builders todo el dia
CQRS/ED(Event Driven)/ActorModel/CA(Clean Architecture): Patrones y arquitecturas
BC/Bounded Context: Para hacerlo simple se podria decir que son un conjunto de aggregates y reglas de dominio separados por aplicacion (Se puede ser mas denso con la explicacion)