domain driven design - que - Comunicación entre dos contextos acotados en DDD
que es ddd (5)
Al integrar BCs, tienes algunas opciones. La razón por la que se desaconseja llamar a un BC externo es porque requiere que ambos BC estén operativos al mismo tiempo. Sin embargo, esto es a menudo bastante aceptable y es más simple que la alternativa. Una alternativa es hacer que el Juego BC se suscriba a los eventos del Usuario BC y mantenga copias locales de los datos que necesita, que en este caso es información sobre si un usuario es un líder de grupo. De esta manera, cuando el Juego BC debe determinar si un usuario es un líder de grupo, no necesita llamar al Usuario BC, solo lee los datos almacenados localmente. El desafío de esta alternativa event-driven eventos es sincronizar los eventos. Debe asegurarse de que el Juego BC reciba todos los eventos apropiados del Usuario BC. Otro desafío es lidiar con la consistencia eventual , ya que los BC pueden estar ligeramente desincronizados en cualquier momento dado.
CQRS es algo ortogonal a este problema.
Tengo pocos Contextos Bounded diferentes en el dominio. La validación de una operación CRUD se construye en cada Contexto Bounded.
Por ejemplo, puedo crear una entidad llamada JUEGO solo si la persona que la crea es un líder de grupo .
Tengo dos Contextos delimitados (BC) en este ejemplo. Uno es el juego BC y el otro es el usuario BC . Para resolver el problema, en el Juego BC , tengo que hacer una llamada de servicio de dominio como IsGroupLeader () al Usuario BC antes de proceder a crear el Juego.
No creo que este tipo de comunicación sea recomendado por DDD. También puedo tener una entidad de usuario en el juego BC , pero no quiero porque la misma entidad de usuario se usa de manera diferente en un contexto diferente en un BC diferente.
Mis preguntas son:
¿Debo usar eventos de dominio donde el juego BC tiene que enviar un evento al usuario BC preguntando el estado del usuario ? Con este enfoque, no hago una llamada síncrona como IsGroupLeader sino un evento llamado is_group_leader . Luego, el Juego BC tiene que esperar a que el Usuario BC procese el evento y devuelva el estado. El Juego BC creará la entidad del Juego solo después de que el Usuario BC procese el evento.
¿Es CQRS una solución a mi problema?
Cualquier idea apreciada.
Así es como razonaría al respecto.
Yo diría que el Juego BC no sabe acerca de "Usuarios", sin embargo, podría saber acerca de "Jugadores".
Si el Juego BC depende de un jugador activo / actual, debe pasarse al BC cuando se crea la instancia del Juego BC.
p.ej.
Player currentPlayer = GetPlayerSomehow...();
GameBC gameBC = new GameBC(currentPlayer);
gameBC.DoStuff();
Ahora sus dos BC aún están separados, puede probarlos por separado, etc.
Y para que todo funcione, simplemente haces algo como:
User currentUser = GetCurrentUser();
Player currentPlayer = new Player();
currentPlayer.IsGroupLeader = currentUser.IsGroupLeader;
GameBC gameBC = new GameBC(currentPlayer);
gameBC.DoStuff();
Esto sirve como una capa anticorrupción entre el UserBC y el GameBC, puede mover y validar el estado que desea del UserBC al estado que necesita para su GameBC.
Y si su GameBC necesita acceder a muchos usuarios, aún puede pasar algún tipo de servicio de mapas al juego BC que realiza este tipo de transformación internamente.
Creo que es posible que deba seguir un enfoque diferente en el que haré que la entidad Usuario forme parte del Juego BC (la misma entidad también forma parte del Usuario BC ). Usaré el Repositorio para leer la bandera de IsGroupLeader desde la base de datos en el Juego BC . De esta manera, se elimina la dependencia de la BC de usuario y no se necesita comunicación con la BC de usuario .
¿Qué piensas?
Creo que ya casi estás allí. Cerca de una buena solución. No estoy tan seguro de que tengas que dividir estos dos en dos AC. Su Usuario Aggregateroot (?) Y Juego pueden pertenecer a un BC y depender unos de otros. Un usuario "tiene una" Membresía "a uno o varios" Juegos (solo adivinando las relaciones de su entidad). Pero ahora solo estoy haciendo una lluvia de ideas. Trate de seguir :) A continuación se presentan diferentes enfoques:
First GameBC tiene un método Create () que realmente toma una UserMembership como param. Crear (UserMembership). Luego, a través de la entidad UserMembership, sabrá qué tipo de membresía y este usuario. Si se acepta, se crea el juego. Si no se lanza una excepción o el juego recibe un mensaje de regla roto, depende del enfoque que desee comunicar al cliente. La coordinación se puede hacer en la capa de aplicación sin pérdida de conocimiento del dominio.
Segundo lo haces como una de las otras respuestas. Subes un CreateGameEvent dentro del método Game.Create (UserId). Ese evento es capturado por un EventHandler (registrado por IoC en el inicio de la aplicación) que reside en la capa de la aplicación y busca UserMembership a través del repositorio. La pequeña fuga de conocimiento del dominio es la regla de negocios que sabe quién tiene permiso para hacer lo que se verifica en la capa de aplicación. Esto se puede resolver dejando que CreateGameEventHandler tome UserId y RuleRef (puede ser la cadena "CAN_CREATE_GAME" o enumeración) y dejando que un objeto UserPermission verifique el permiso. Si no. La excepción es lanzada y atrapada en la capa de aplicación. El inconveniente puede ser que usted quiera que las cadenas de referencia de permiso estén codificadas en el método Create.
Tercero ... continúa donde termina el segundo enfoque. Usted sabe que GameBC puede no ser el lugar adecuado para realizar búsquedas de permisos de usuario si sigue el principio de SRP. Pero la acción se desencadena alrededor de ese método de alguna manera. Una alternativa es crear un (GroupLeader usuario). O puede tener Game.Create (usuario usuario) y luego hacer una validación de que el usuario es del tipo GroupLeader. Crear (GroupLeader) le dice qué necesita para llamar a este método.
Último Quizás una alternativa que me gusta más ahora al escribir esto. Cuando quiere crear una entidad, normalmente permito que el método Crear (Guardar) esté en el repositorio. La interfaz IGameRepository se encuentra junto a la entidad de juego en el proyecto de ensamblaje de dominio. Pero también puede crear un GameFactory que sea responsable de iniciar el ciclo de vida de Game Entity. Aquí también es un buen lugar para poner el método Crear ... GameFactory.Create (GroupLeader) {return new Game.OwnerUserId = GroupLeader.Id; } Entonces solo lo guardas IGameRepository.Save (Juego)
Entonces tienes una forma intuitiva y autodescriptiva de decirle a otros desarrolladores que "debes tener una instancia de GroupLeader para crear un juego".
Finalmente, espero que se dé cuenta de que conoce el dominio y que descubra qué es lo que más le conviene. Sé pragmático y no te vayas con el hardcore de Eric Evan. Hay tantos desarrolladores por ahí que están atrapados en una "religión" en cómo se harán las cosas. El tamaño del proyecto, el dinero, el tiempo y las dependencias a otros sistemas también afectan la forma en que puede ser estricto al hacer DDD.
Buena suerte.
Para hacer frente al tipo de problemas que enfrenta, utilizamos roles limitados, un patrón de modelado que surgió a lo largo de los años y que demostró funcionar muy bien. Los contextos delimitados se definen después de unidades semánticas que a menudo, en organizaciones empresariales, pueden asignarse a roles específicos.
Eso debería ser obvio considerando que los diferentes roles enfrentan diferentes problemas y, por lo tanto, hablan idiomas ligeramente diferentes (o totalmente).
Por lo tanto, siempre modelamos los roles que interactúan con nuestra aplicación como un punto de unión entre los requisitos aplicativos (infraestructura, persistencia, UI, localización, etc.) y las reglas de negocios (el dominio) y los codificamos en diferentes módulos (también conocidos como ensamblados o paquetes).
En cuanto a su segunda pregunta, CQRS puede ser una forma de codificar el tipo de interacciones entre los BC que usted describe, pero no me gusta en este contexto particular.