2009-10-04 27 views
16

Implementé el lado del comando de DDD utilizando el modelo de dominio y los repositorios, pero ¿cómo implemento el lado de la consulta?¿Cómo implementar el lado de consulta de CQRS en DDD?

¿Creé un modelo de dominio completamente nuevo para la interfaz de usuario, y dónde se guarda esto en la estructura del proyecto ... en la capa de dominio, la capa de interfaz de usuario, etc.?

Además, ¿qué uso como mecanismo de consulta, creo nuevos repositorios específicamente para los objetos de dominio UI, algo que no sean repositorios, o algo más?

+3

Acabo de encontrar una publicación de blog de Jak Charlton que describe por qué decidió no usar DDD para consultar en absoluto: "El lado de la consulta de CQS no necesita entidades fuertemente tipadas, ni requiere DTO fuertemente tipadas - dado que se trata en gran medida de datos ad-hoc que mantienen estas entidades y los DTO consumirían una cantidad desproporcionada de tiempo de desarrollo para algo que un DataTable pueda manejar más que adecuadamente " http://devlicio.us/blogs/casey/archive/2009/ 06/22/we-are-not-doing-ddd-part-two-cqs.aspx – rohancragg

Respuesta

6

Según mi comprensión de CQRS, crearía un conjunto de DTO que cumplen los requisitos de las pantallas de la interfaz de usuario o las aplicaciones que pueden necesitar consumirlas.

El hecho de que exista en el proyecto se basa en los requisitos, ya que dependería de exponer estos DTO a través de servicios web. En cuyo caso yo no lo pondría en la Capa Web, sino en la capa de Aplicación o en una capa de Fachada dedicada.

Luego, tendría un repositorio de solo lectura o una capa de acceso a datos que llene los DTO directamente. Creo que el lado de Query debe optimizarse para el rendimiento de lectura, en cuyo caso las consultas directas/procedimientos almacenados en vistas o tablas de base de datos y SqlDataReaders harían el mejor trabajo aquí. Pero definitivamente valdría la pena abstraer este acceso detrás de una interfaz para que pueda agregar una implementación en caché más adelante en la pista.

Si está utilizando un ORM y desea asignar sus Entidades de Dominio a los DTO, entonces podría tener un QueryRepository genérico que tenga métodos que tomen una especificación IS o una construcción similar para definir sus consultas, luego un objeto DtoAssembler para crear la Dtos de tus objetos de Dominio. Luego tenga una implementación con un objeto de primera clase para cada una de las consultas que va a realizar.

Aquí está un ejemplo bastante artificial, pero espero que te da una idea.

 public interface ISpecification<T> 
     { 
      Expression<Func<T, bool>> Predicate { get; } 

     } 

     public class ActiveCustomersSpecification : ISpecification<Customer> 
     { 
      private Expression<Func<Customer, bool>> predicate; 
      public ActiveCustomersSpecification() 
      { 
       predicate = c => c.IsActive; 
      } 
      #region ISpecicfication<Customer> Members 

      public Expression<Func<Customer, bool>> Predicate 
      { 
       get { return predicate; } 
      } 

      #endregion 
     } 

     public interface IQueryRepository<T> 
     { 
      IQueryable<T> GetQuery(ISpecification<T> specification); 

      IEnumerable<T> FindAllBy(ISpecification<T> specification); 
     } 



public class CustomerDtoAssembler 
    { 
     public CustomerDto AssembleFrom(Customer customer) 
     { 
      var customerDto = new CustomerDto 
      { 
       Id = customer.Id 
      }; 

      return customerDto; 
     } 
    } 
+0

Descubrí que es interesante, ¿pueden aclarar una cosa? el repositorio de consultas y la interfaz de especificación, ambos están claramente relacionados con la tecnología ORM, ¿no? En este caso, supongo que el marco de la entidad. ¿No debería ser abstracto? –

3

creo willbt le ha dado una muy buena starting point.

Yo agregaría que si opta por continuar utilizando el ORM como la estrategia de acceso a datos para consultas, sería recomendable que considere la definición de una estrategia de búsqueda adaptada a los datos que espera que necesite acceder (Estoy pensando específicamente en NHibernate aquí, por cierto). Lo que esto significa es que usted puede decidir si desea perezoso carga o ansiosos-cargar los objetos y colecciones asociadas a un objeto particular Aggregate Root.

El NCommon project de Ritesh Rao ofrece una excelente demostración (en curso) de cómo definir una estrategia de búsqueda diferente para diferentes propósitos.

Ritesh explains it really bien en su blog.

Seguir adelante y echar un vistazo a la fuente:

En los Repository_For_Uses_Registered_Fetching_Strategies 'prueba la llamada a

NHRepository<Order>().For<NHRepositoryTests>() 

... causa la fe las estrategias de tching registradas contra la clase NHRepositoryTests que se utilizarán, y por lo tanto, se cargarán ansiosamente los artículos de pedido y los productos sin interferir con las configuraciones de asignación de NHibernate.

+1

Este es un punto válido. Sin embargo, solo se aplica realmente si devuelve sus Entidades a su capa Fachada y luego asigna a DTOs. Mi sugerencia aquí sería usar el IQueryable devuelto y proyectar directamente en los DTO. De esta manera evitará cualquier estrategia de obtención. Incluso puede hacer proyecciones en el nivel de mapeo de NHibernate. – willbt

+0

¡Por supuesto! Si gracias. Todavía estoy llegando a un acuerdo sobre cómo NHibernate.Linq cambia el juego, pero si nos fijamos en cómo Ritesh está adoptando IQueryable, verá que certinalmente 'lo consigue' y está construyendo cosas buenas a su alrededor (es decir, las especificaciones que toman una Expresión en su constructor). – rohancragg

+0

enlaces ahora actualizados en las ubicaciones de GitHub – rohancragg