repositorio patron generico nhibernate linq-to-sql orm domain-driven-design ddd-repositories

nhibernate - generico - patron repositorio



Pregunta sobre los repositorios y sus métodos de guardado para objetos de dominio (2)

Tengo una pregunta algo ridícula con respecto a DDD, Repository Patterns y ORM. En este ejemplo, tengo 3 clases: Dirección , Compañía y Persona . Una persona es miembro de una empresa y tiene una dirección. Una compañía también tiene una dirección.

Estas clases reflejan un modelo de base de datos. Eliminé todas las dependencias de mis Modelos, por lo que no están vinculadas a una biblioteca ORM particular, como NHibernate o LinqToSql. Estas dependencias se tratan dentro de los repositorios.

Dentro de uno de los repositorios hay un método SavePerson (Persona persona) que inserta / actualiza una Persona dependiendo de si ya existe en la base de datos.

Como un objeto Persona tiene una Compañía, actualmente guardo / actualizo los valores de la propiedad de la Compañía al hacer esa llamada a SavePerson. Inserté / actualicé todos los datos de la Compañía - Nombre y Dirección - durante este procedimiento.

Sin embargo, realmente me cuesta pensar en un caso en el que los datos de una empresa pueden cambiar al tratar con una persona: solo deseo poder asignar una empresa a una persona o mover una persona a otra empresa. No creo que quiera crear una nueva compañía junto a una nueva persona. Entonces, las llamadas a SaveCompany introducen llamadas innecesarias a la base de datos. Al guardar una Persona, debería poder actualizar la columna CompanyId.

Pero dado que la clase Persona tiene una propiedad de la Compañía, me siento algo inclinado a actualizarla / insertarla. Desde un punto de vista estricto / puro, el método SavePerson debería guardar la Persona completa.

¿Cuál sería la forma preferida de ser? ¿Simplemente insertando / actualizando el Id de la Compañía de la propiedad de la Compañía al guardar una Persona o al guardar todos sus datos? ¿O crearía dos métodos distintos para ambos escenarios (¿cómo los denominaría?)

Además, otra pregunta, actualmente tengo distintos métodos para guardar una Persona, una Dirección y una Compañía, así que cuando guardo una Compañía, también llamo a SaveAddress. Supongamos que utilizo LinqToSql, esto significa que no inserto / actualizo la Compañía y la Dirección en la misma consulta de Linq. Supongo que hay 2 Seleccionar llamadas (verifica si existe una empresa, verificando si existe una dirección). Y luego dos llamadas Insert / Update para ambos. Aún más si se introducen más clases de modelos compuestos. ¿Hay alguna manera para que LinqToSql optimice estas llamadas?

public class Address { public int AddressId { get; set; } public string AddressLine1 { get; set; } public string AddressLine2 { get; set; } public string City { get; set; } public string PostalCode { get; set; } } public class Company { public int CompanyId { get; set; } public string Name { get; set; } public Address Address { get; set; } } public class Person { public int PersonId { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public string Email { get; set; } public Company Company { get; set; } public Address Address { get; set; } }

Editar

También vea esta pregunta de seguimiento . ¿Cómo se almacenan los objetos de valor en una base de datos?


A partir de mi experiencia reciente en el uso del patrón de repositorio, creo que se beneficiaría de utilizar un repositorio genérico, el IRepository de T. ahora común. De esta forma, no tendría que agregar métodos de depósito como SavePerson (Person person). En cambio, tendrías algo así como:

IRepository<Person> personRepository = new Repository<Person>(); Person realPerson = new Person(); personRepository.SaveOrUpdate(realPerson);

Este método también se presta bien para probar el desarrollo impulsado y la burla.

Creo que las preguntas sobre el comportamiento en su descripción serían preocupaciones para el Dominio, tal vez debería tener un método AddCompany en su clase Person y cambiar las propiedades de la Compañía a

public Company Company { get; private set; }

Mi punto es; modelar el dominio sin preocuparse por cómo se conservarán los datos en la base de datos. Esta es una preocupación para el servicio que usará su modelo de dominio.

De vuelta al Repositorio, eche un vistazo a esta publicación para obtener una buena explicación de IRepository sobre LinqToSql. El blog de Mike tiene muchas otras publicaciones sobre repositorios. Cuando llegas a elegir un ORM puedo recomendar HHibernate sobre LinqToSql, este último ya no existe y NHibernate tiene una gran comunidad de soporte.


Yo mismo he usado el enfoque de IRepository últimamente que sugiere Keith. Pero, no deberías centrarte en ese patrón aquí. En cambio, hay algunas piezas más en el libro de estrategias de DDD que se pueden aplicar aquí.

Use objetos de valor para sus direcciones

En primer lugar, existe el concepto de Value Objects (VO) que puede aplicar aquí. En tu caso, sería la Dirección. La diferencia entre un objeto de valor y un objeto de entidad es que las entidades tienen una identidad; VOs no. La identidad del VO realmente es la suma de sus propiedades, no una identidad única. En el libro Domain-Drive Design Quickly (también es una descarga gratuita de PDF), explica esto muy bien al afirmar que una dirección es solo un punto en la Tierra y no necesita una identidad similar a SocialSecurity como una persona. Ese punto en la Tierra es la combinación de la calle, el número, la ciudad, el código postal y el país. Puede tener valores de latitud y longitud, pero aún así son incluso VO por definición porque es una combinación de dos puntos.

Use los servicios para combinar sus entidades en una única entidad para actuar.

Además, no olvide el concepto de Servicios en el libro de estrategias de DDD. En su ejemplo, ese servicio sería:

public class PersonCompanyService { void SavePersonCompany(IPersonCompany personCompany) { personRepository.SavePerson(); // do some work for a new company, etc. companyRepository.SaveCompany(); } }

Existe la necesidad de un servicio cuando tiene dos entidades que necesitan ambas y necesitan una acción similar para coordinar una combinación de otras acciones. En su caso, guardando una Persona () y creando una Compañía en blanco () al mismo tiempo.

Los ORM usualmente requieren una identidad, punto.

Ahora, ¿cómo harías para guardar la dirección VO en la base de datos? Usará un IAddressRepository obviamente. Pero como la mayoría de los ORM (es decir, LingToSql) requieren que todos los objetos tengan una Identidad, este es el truco: Marque la identidad como interna en su modelo, para que no quede expuesta fuera de su capa de Modelo. Este es el consejo de Steven Sanderson.

public class Address { // make your identity internal [Column(IsPrimaryKey = true , IsDbGenerated = true , AutoSync = AutoSync.OnInsert)] internal int AddressID { get; set; } // everything else public [Column] public string StreetNumber { get; set; } [Column] public string Street { get; set; } [Column] public string City { get; set; } ... }