Según tengo entendido, en DDD, es apropiado usar un patrón de repositorio con una raíz agregada. Mi pregunta es, ¿debo devolver los datos como una entidad u objetos de dominio / DTO?
Quizás algún código explique más mi pregunta:
Entidad
public class Customer
{
public Guid Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
¿Debo hacer algo como esto?
public Customer GetCustomerByName(string name) { /*some code*/ }
¿O algo como esto?
public class CustomerDTO
{
public Guid Id { get; set; }
public FullName { get; set; }
}
public CustomerDTO GetCustomerByName(string name) { /*some code*/ }
Pregunta adicional
- En un repositorio, ¿debo devolver IQueryable o IEnumerable?
- En un servicio o repositorio, debería hacer algo así ..
GetCustomerByLastName
,GetCustomerByFirstName
,GetCustomerByEmail
? o simplemente hacer un método similarGetCustomerBy(Func<string, bool> predicate)
?
GetCustomerByName('John Smith')
devolverá si tiene veinte John Smiths en su base de datos? Parece que estás asumiendo que no hay dos personas que tengan el mismo nombre.Respuestas:
Bueno, eso depende completamente de sus casos de uso. La única razón por la que puedo pensar en devolver un DTO en lugar de una entidad completa es si su entidad es enorme y solo necesita trabajar en un subconjunto de ella.
Si este es el caso, entonces quizás debería reconsiderar su modelo de dominio y dividir su entidad grande en entidades más pequeñas relacionadas.
Una buena regla general es devolver siempre el tipo más simple (el más alto en la jerarquía de herencia) posible. Por lo tanto, regrese a
IEnumerable
menos que desee dejar que el consumidor del repositorio trabaje con unIQueryable
.Personalmente, creo que devolver un
IQueryable
mensaje es una abstracción permeable, pero he conocido a varios desarrolladores que argumentan apasionadamente que no lo es. En mi opinión, toda la lógica de consulta debe estar contenida y oculta por el Repositorio. Si permite que el código de llamada personalice su consulta, ¿cuál es el punto del repositorio?Por la misma razón que mencioné en el punto 1, definitivamente no lo use
GetCustomerBy(Func<string, bool> predicate)
. Puede parecer tentador al principio, pero esta es exactamente la razón por la cual la gente ha aprendido a odiar los repositorios genéricos. Es permeable.Cosas como
GetByPredicate(Func<T, bool> predicate)
solo son útiles cuando están ocultos detrás de clases concretas. Por lo tanto, si tuviera una clase base abstracta llamadaRepositoryBase<T>
que expuso yprotected T GetByPredicate(Func<T, bool> predicate)
que solo fue utilizada por repositorios concretos (por ejemplo,public class CustomerRepository : RepositoryBase<Customer>
) entonces estaría bien.fuente
Hay una comunidad considerable de personas que usan CQRS para implementar sus dominios. Mi sensación es que, si la interfaz de su repositorio es análoga a las mejores prácticas utilizadas por ellos, no se desviará demasiado.
Basado en lo que he visto ...
1) Los manejadores de comandos usualmente usan el repositorio para cargar el agregado a través de un repositorio. Los comandos se dirigen a una única instancia específica del agregado; el repositorio carga la raíz por ID. No hay, por lo que puedo ver, un caso en el que los comandos se ejecuten contra una colección de agregados (en su lugar, primero ejecutaría una consulta para obtener la colección de agregados, luego enumeraría la colección y emitiría un comando para cada uno.
Por lo tanto, en contextos en los que va a modificar el agregado, esperaría que el repositorio devuelva la entidad (también conocida como raíz del agregado).
2) Los manejadores de consultas no tocan los agregados en absoluto; en cambio, trabajan con proyecciones de los agregados: objetos de valor que describen el estado de los agregados / agregados en algún momento. Entonces piense en ProjectionDTO, en lugar de AggregateDTO, y tiene la idea correcta.
En contextos en los que va a ejecutar consultas contra el agregado, prepararlo para su visualización, etc., esperaría ver un DTO, o una colección de DTO, devuelta, en lugar de una entidad.
Todas sus
getCustomerByProperty
llamadas me parecen consultas, por lo que caerían en la última categoría. Probablemente quiera usar un único punto de entrada para generar la colección, por lo que estaría buscando para ver sies una elección razonable; los manejadores de consultas luego construirían la especificación apropiada a partir de los parámetros dados y pasarían esa especificación al repositorio. La desventaja es que la firma realmente sugiere que el repositorio es una colección en memoria; No está claro para mí que el predicado te compre mucho si el repositorio es solo una abstracción de ejecutar una instrucción SQL en una base de datos relacional.
Sin embargo, hay algunos patrones que pueden ayudar. Por ejemplo, en lugar de construir la especificación a mano, pase al repositorio una descripción de las restricciones y permita que la implementación del repositorio decida qué hacer.
Advertencia: Java como mecanografía detectado
En conclusión: la elección entre proporcionar un agregado y proporcionar un DTO depende de lo que espera que el consumidor haga con él. Mi conjetura sería una implementación concreta que soporte una interfaz para cada contexto.
fuente
getCustomersThatSatisfy(Specification spec)
lista, enumeraré las propiedades que necesitaba para las opciones de búsqueda. ¿Lo estoy entendiendo bien?Según mi conocimiento de DDD, deberías tener esto,
Depende, usted encontrará opiniones mixtas de diferentes personas para esta pregunta. Pero personalmente creo que si su repositorio será utilizado por un servicio como CustomerService, entonces puede usar IQueryable, de lo contrario, quédese con IEnumerable.
¿Deben los repositorios devolver IQueryable?
En su repositorio debe tener una función genérica como dijo,
GetCustomer(Func predicate)
pero en su capa de servicio agregue tres métodos diferentes, esto se debe a que existe la posibilidad de que su servicio sea llamado desde diferentes clientes y necesiten diferentes DTO.También puede usar el patrón de repositorio genérico para CRUD común, si aún no lo sabe.
fuente