net framework ddd business c# asp.net entity-framework entity-framework-4 domain-driven-design

ddd - business logic layer c# entity framework



Reducción de repositorios para agregar raíces (5)

Esta es una vieja pregunta, pero pensó que valía la pena publicar una solución simple.

  1. EF Context ya le proporciona tanto la Unidad de trabajo (cambios de pistas) como los Repositorios (referencia en memoria a cosas de DB). La abstracción adicional no es obligatoria.
  2. Elimine el DBSet de su clase de contexto, ya que Phone no es una raíz agregada.
  3. En su lugar, use la propiedad de navegación ''Teléfonos'' en Usuario.

static void updateNumber (int userId, string oldNumber, string newNumber)

static void updateNumber(int userId, string oldNumber, string newNumber) { using (MyContext uow = new MyContext()) // Unit of Work { DbSet<User> repo = uow.Users; // Repository User user = repo.Find(userId); Phone oldPhone = user.Phones.Where(x => x.Number.Trim() == oldNumber).SingleOrDefault(); oldPhone.Number = newNumber; uow.SaveChanges(); } }

Actualmente tengo un repositorio para casi todas las tablas de la base de datos y me gustaría alinearme aún más con DDD al reducirlas para agregar raíces solamente.

Supongamos que tengo las siguientes tablas, User y Phone . Cada usuario puede tener uno o más teléfonos. Sin la noción de raíz agregada, podría hacer algo como esto:

//assuming I have the userId in session for example and I want to update a phone number List<Phone> phones = PhoneRepository.GetPhoneNumberByUserId(userId); phones[0].Number = “911”; PhoneRepository.Update(phones[0]);

El concepto de raíces agregadas es más fácil de entender en el papel que en la práctica. Nunca tendré números de teléfono que no pertenezcan a un Usuario, entonces ¿tendría sentido eliminar el PhoneRepository e incorporar métodos relacionados con el teléfono en el UserRepository? Suponiendo que la respuesta es sí, voy a reescribir la muestra del código anterior.

¿Se me permite tener un método en el UserRepository que devuelva números telefónicos? O siempre debe devolver una referencia a un Usuario, y luego recorrer la relación a través del Usuario para llegar a los números de teléfono:

List<Phone> phones = UserRepository.GetPhoneNumbers(userId); // Or User user = UserRepository.GetUserWithPhoneNumbers(userId); //this method will join to Phone

Independientemente de la forma en que adquiera los teléfonos, suponiendo que modifique uno de ellos, ¿cómo puedo actualizarlos? Mi entendimiento limitado es que los objetos debajo de la raíz se deben actualizar a través de la raíz, lo que me guiaría hacia la opción n. ° 1 a continuación. Aunque esto funcionará perfectamente con Entity Framework, esto parece extremadamente poco descriptivo, porque al leer el código no tengo idea de qué estoy actualizando en realidad, a pesar de que Entity Framework mantiene tabuladas los objetos cambiados dentro del gráfico.

UserRepository.Update(user); // Or UserRepository.UpdatePhone(phone);

Por último, suponiendo que tengo varias tablas de búsqueda que no están realmente vinculadas a nada, como CountryCodes , ColorsCodes , SomethingElseCodes . Podría usarlos para llenar menús desplegables o por cualquier otra razón. ¿Son estos repositorios independientes? ¿Se pueden combinar en algún tipo de agrupación / depósito lógico como CodesRepository ? O es eso en contra de las mejores prácticas.


Se le permite tener cualquier método que desee en su repositorio :) En los dos casos que menciona, tiene sentido devolver al usuario con la lista de teléfonos poblada. Normalmente, el objeto del usuario no se completará por completo con toda la información secundaria (por ejemplo, todas las direcciones, números de teléfono) y es posible que tengamos diferentes métodos para que el objeto del usuario se llene con otro tipo de información. Esto se conoce como carga diferida.

User GetUserDetailsWithPhones() { // Populate User along with Phones }

Para la actualización, en este caso, el usuario se está actualizando, no el número de teléfono en sí. El modelo de almacenamiento puede almacenar los teléfonos en una tabla diferente y de esa manera usted puede pensar que solo se están actualizando los teléfonos, pero ese no es el caso si se piensa desde la perspectiva de DDD. En lo que respecta a la legibilidad, mientras que la línea

UserRepository.Update(user)

solo no transmite lo que se está actualizando, el código que está sobre él dejaría en claro lo que se está actualizando. También es probable que sea parte de una llamada de método de front-end que pueda indicar qué se está actualizando.

Para las tablas de búsqueda, y en realidad, incluso de lo contrario, es útil tener GenericRepository y usar eso. El repositorio personalizado puede heredar de GenericRepository.

public class UserRepository : GenericRepository<User> { IEnumerable<User> GetUserByCustomCriteria() { } User GetUserDetailsWithPhones() { // Populate User along with Phones } User GetUserDetailsWithAllSubInfo() { // Populate User along with all sub information e.g. phones, addresses etc. } }

Busque el marco de entidad de repositorio genérico y corregirá muchas implementaciones agradables. Usa uno de esos o escribe el tuyo.


Si el teléfono no tiene sentido sin el usuario, es una entidad (si le importa su identidad) u objeto de valor y siempre debe modificarse a través del usuario y recuperarse / actualizarse conjuntamente.

Piense en las raíces agregadas como definidores de contexto: ellos dibujan contextos locales pero se encuentran en un contexto global (su aplicación).

Si sigue el diseño impulsado por dominio, se supone que los repositorios son 1: 1 por raíces agregadas.
No hay excusas.

Apuesto a que estos son problemas que enfrentas:

  • dificultades técnicas - desajuste de la impedancia de la relación del objeto. Usted está luchando con persistentes gráficos de objetos enteros con facilidad y el tipo de marco de entidad no puede ayudar.
  • el modelo de dominio está centrado en los datos (a diferencia del comportamiento centrado). por eso, pierde conocimiento sobre la jerarquía de objetos (contextos previamente mencionados) y mágicamente todo se convierte en una raíz agregada.

No estoy seguro de cómo solucionar el primer problema, pero me he dado cuenta de que arreglar el segundo correcciones primero es lo suficientemente bueno. Para entender lo que quiero decir con comportamiento centrado, pruebe este artículo .

Ps Reducir el repositorio a la raíz agregada no tiene sentido.
Pps Evita "CodeRepositories" . Eso conduce a datos centrados -> código de procedimiento.
Ppps Evitar el patrón de unidad de trabajo. Las raíces agregadas deben definir los límites de las transacciones.


Si una entidad de teléfono solo tiene sentido junto con un usuario raíz agregado, también creo que tiene sentido que la operación para agregar un nuevo registro de teléfono sea responsabilidad del objeto de dominio de usuario a través de un método específico (comportamiento de DDD) y que podría tiene mucho sentido por varias razones, la razón inmediata es que debemos verificar que el objeto de usuario exista ya que la entidad de teléfono depende de su existencia y mantener un bloqueo de transacción mientras se realizan más comprobaciones de validación para garantizar que ningún otro proceso haya eliminado el agregado raíz antes. hemos terminado de validar la operación. En otros casos con otros tipos de agregados raíz, puede agregar o calcular algún valor y persistir en las propiedades de columna del agregado raíz para un procesamiento más eficiente por parte de otras operaciones más adelante. Aunque tenga en cuenta que el objeto Dominio del usuario tiene un método que agrega el teléfono, no significa que deba saber sobre la existencia de la base de datos o EF, una de las características principales de EM e Hibernate es que pueden rastrear los cambios realizados en la entidad clases de forma transparente y que también significa la adición de nuevas entidades relacionadas por sus propiedades de recopilación de navegación.

Además, si desea utilizar métodos que recuperen todos los teléfonos, independientemente de los usuarios que los posean, podría hacerlo a través del repositorio de usuarios. Solo necesita un método que devuelva todos los usuarios como IQueryable, luego puede asignarlos para obtener todos los teléfonos de los usuarios y hacer un refinado consulta con eso. Entonces, ni siquiera necesita un PhoneRepository en este caso. Además, preferiría utilizar una clase con el método de extensiones para IQueryable que pueda usar en cualquier lugar, no solo desde una clase de repositorio, si quisiera abstraer consultas detrás de los métodos.

Solo una advertencia para poder eliminar entidades telefónicas utilizando solo el objeto de dominio y no un repositorio de teléfono que necesita para asegurarse de que UserId es parte de la clave primaria del teléfono o, en otras palabras, la clave principal de una grabación de teléfono es una clave compuesta compuesto por UserId y alguna otra propiedad (sugiero una identidad generada automáticamente) en la entidad Phone. Esto tiene sentido de manera intuitiva ya que la grabación del teléfono es "propiedad" del registro del usuario y su eliminación de la colección de navegación del usuario equivaldría a su eliminación completa de la base de datos.


Su ejemplo en el repositorio Aggregate Root está perfectamente bien, es decir, cualquier entidad que no pueda existir razonablemente sin dependencia de otra no debería tener su propio repositorio (en su caso, el teléfono). Sin esta consideración, puede encontrarse rápidamente con una explosión de repositorios en una asignación de 1-1 a tablas db.

Debería considerar el uso del patrón Unidad de trabajo para cambios en los datos en lugar de los repositorios en sí, ya que creo que le causan cierta confusión sobre la intención cuando se trata de cambios persistentes en el DB. En una solución EF, la Unidad de trabajo es esencialmente una envoltura de interfaz alrededor de su Contexto EF.

Con respecto a su repositorio de datos de búsqueda, simplemente creamos un ReferenceDataRepository que se hace responsable de los datos que no pertenecen específicamente a una entidad de dominio (Países, Colores, etc.).