2012-06-30 18 views
11

Estoy construyendo una aplicación con un modelo de dominio usando CQRS y conceptos de eventos de dominio (pero no fuente de eventos, simplemente SQL antiguo). No hubo ningún problema con los eventos de tipo SomethingChanged. Luego me quedé atrapado en la implementación de eventos SomethingCreated.ID de entidad nueva en el evento de dominio

Cuando creo una entidad que está mapeada en una tabla con clave primaria de identidad, entonces no sé el Id hasta que la entidad persista. La entidad ignora la persistencia, por lo que cuando se publica un evento desde el interior de la entidad, no se conoce el Id; se configura mágicamente después de llamar a context.SaveChanges() solamente. Entonces, ¿cómo/dónde/cuándo puedo poner el Id en los datos del evento?

Estaba pensando:

  • La inclusión de la referencia a la entidad en el evento. Eso funcionaría dentro del dominio pero no necesariamente en un entorno distribuido con múltiples sistemas autónomos que se comunican por eventos/mensajes.
  • Anulando SaveChanges() para actualizar de alguna manera los eventos en cola para la publicación. Pero los eventos están destinados a ser inmutables, así que esto parece muy sucio.
  • Deshacerse de los campos de identidad y usar los GUID generados en el constructor de la entidad. Esto podría ser el más fácil, pero podría afectar el rendimiento y dificultar otras cosas, como la depuración o la consulta (where id = 'B85E62C3-DC56-40C0-852A-49F759AC68FB', no MIN, MAX etc.). Eso es lo que veo en muchas aplicaciones de muestra.
  • Enfoque híbrido: deje la identidad y utilícela principalmente para claves externas y uniones más rápidas, pero use GUID como el identificador único por el cual extraigo las entidades del repositorio en la aplicación.
+3

No incluya una referencia a la entidad en el evento. Está bien insertar la identificación después de guardar. Debe considerarse inmutable al momento de la publicación. Sin embargo, esto podría terminar siendo problemático si va a permitir reintentos dentro del contexto de producción del evento.Hay un valor en la asignación de identificadores desde el exterior, tanto que creo que esta debería ser una estrategia predeterminada. Entonces, tanto el uso de GUID como el enfoque híbrido podrían funcionar. Otro enfoque podría ser algún mecanismo HILO o algo así como copo de nieve. –

+0

¿Podría elaborar sobre HILO y copo de nieve? – Pein

+0

http://nhforge.org/blogs/nhibernate/archive/2009/03/20/nhibernate-poid-generators-revealed.aspx y http://engineering.twitter.com/2010/06/announcing-snowflake.html –

Respuesta

9

Personalmente, me gustan los GUID para identificadores únicos, especialmente en entornos distribuidos multiusuario donde los identificadores numéricos causan problemas. Como tal, nunca utilizo columnas/propiedades de identidad generadas por la base de datos y este problema desaparece.

Además de eso, ya que está siguiendo CQRS, sin duda tiene un CreateSomethingCommand y CreateSomethingCommandHandler correspondiente que realmente lleva a cabo los pasos necesarios para crear la nueva instancia y persistir el nuevo objeto utilizando el repositorio (a través de context.SaveChanges). Voy a plantear el evento SomethingCreated aquí en lugar de en el objeto de dominio en sí.

Por un lado, esto resuelve su problema porque el controlador de comandos puede esperar a que se complete la operación de la base de datos, extraer el valor de identidad, actualizar el objeto y pasar la identidad en el evento. Pero, lo que es más importante, también aborda la complicada cuestión de cuándo se 'creó' exactamente el objeto.

Crear un evento de dominio en el constructor es una mala práctica ya que los constructores deben ser delgados y simplemente realizar la inicialización. Además, en su modelo, el objeto realmente no se crea hasta que tiene una ID asignada. Esto significa que hay pasos de inicialización adicionales necesarios después de que se haya ejecutado el constructor. Si tiene más de un paso, ¿impone el orden de ejecución (otro antipatrón) o marca cada uno para reconocer cuándo están completos (ooh, maloliente)? Con suerte, se puede ver cómo esto puede rápidamente irse de las manos.

Por lo tanto, mi recomendación es plantear el evento desde el controlador de comandos. (NOTA: Incluso si cambia a identificadores GUID, seguiría este enfoque porque nunca debería generar eventos de constructores).

+5

"Por lo tanto, mi recomendación es plantear el evento desde el controlador de comandos" => Los eventos deben formar parte del modelo de dominio. En lugar de un constructor deficiente, para aumentar el significado, crearía una fábrica estática: 'crear' para plantear el evento en caso de éxito. – Mik378

+0

"Por un lado, esto resuelve su problema porque el controlador de comando puede esperar a que se complete la operación de la base de datos, saque el valor de identidad". ¿Qué ocurre si se produce un tiempo de espera? ¿Se crean los objetos pero el controlador genera una excepción? En aras de la simplicidad, me gustaría implementar el controlador de comandos como UoW. En tal caso, debe usar Guid como identificador de entidad en lugar de identificador de entidad como campo de base de datos de identidad. –

Cuestiones relacionadas