design patterns - what - ¿Es incorrecto hacer que los objetos de dominio tengan conocimiento de la capa de acceso a datos?
repository pattern que es (4)
Después de leer un poco más y buscar un patrón apropiado, me encontré con el patrón de repositorio .
Por lo que puedo ver, esta es precisamente la solución prevista que permite que los Objetos del Dominio, como Person, deleguen adecuadamente las consultas en el Data Mapper apropiado, mientras que dejan el Objeto del Dominio y Data Mapper completamente abstraídos.
Actualmente estoy trabajando en la reescritura de una aplicación para usar Data Mappers que abstraen completamente la base de datos de la capa de Dominio. Sin embargo, ahora me pregunto cuál es el mejor enfoque para manejar las relaciones entre los objetos de Dominio:
- Llame al método find () necesario del correlacionador de datos relacionado directamente dentro del objeto de dominio
- Escriba la lógica de relación en el mapeador de datos nativo (que es lo que tienden a hacer los ejemplos en PoEAA) y luego invoque la función del correlacionador de datos nativo dentro del objeto de dominio.
O me parece que para preservar el mantra ''Fat Model, Skinny Controller'', los objetos de dominio deben conocer los correlacionadores de datos (ya sean propios o que tengan acceso a los otros mapeadores en el sistema) . Además, parece que la Opción 2 complica innecesariamente la capa de acceso a datos, ya que crea una lógica de acceso a la tabla en múltiples mapeadores de datos en lugar de confinarla a un único mapeador de datos.
Entonces, ¿es incorrecto hacer que los objetos de dominio estén al tanto de los correlacionadores de datos relacionados y llamar a las funciones del correlacionador de datos directamente desde los objetos del dominio?
Actualización: estas son las dos únicas soluciones que puedo imaginar para manejar el tema de las relaciones entre objetos de dominio. Cualquier ejemplo que muestre un mejor método sería bienvenido.
Sí. Pregúntese por qué un objeto de dominio sabe acerca de tal cosa? Ni siquiera por qué, pero ¿cómo? ¿Vas a inyectar tu DAL en tu objeto de Dominio?
El dominio debería seguir a SRP, simplemente vive todo lo demás. Cuando atraviesas tu dominio, no debes saber si esas propiedades se rellenaron mediante carga diferida o si se hidrataron a partir de la creación de instancias.
He escrito un modelo de dominio que tenía objetos DAL en ellos y era una pesadilla para mantener. Luego aprendí NHibernate y mi dominio consiste en POCO y su lógica empresarial respectiva que quiero encapsular.
[Editar]
Aquí hay más información. Solo me avergonzaría si tratara de explicarlo. Solo puedo hablar de la implementación como usuario. Aquí hay un excelente artículo sobre la administración del Modelo de Dominio . Lo que le interesa es la implementación de interceptores y mixins.
Con esas herramientas puede escribir una clase de empleado de la siguiente manera:
public class Employee
{
public Employee()
{
Skills = new List<Skill>();
}
public string Name { get; set; }
public IList<Skill> Skills { get; private set; }
public void AddSkill(Skill skill)
{
// Perform Domain specific validation here...
Skills.Add(skill);
}
}
Como puede ver, mis necesidades de acceso a datos no imponen en mi diseño de dominio en absoluto.
No estoy de acuerdo, creo que el objeto de dominio puede acceder a los repositorios a través de Abstract Factory.
public class Client
{
public void ChangeZipCode(string zipCode)
{
// This method access memory or database depending on the factory context
bool zipCodeExists = RepositoryFactory.Get<IZipCode>().Exists(zipCode);
this.zipCode = zipCode;
}
}
Al utilizar este patrón, no es necesario inyectar interfaces de repositorio en todo su código, sino solo en la fábrica de repositorios.
public abstract class RepositoryFactory
{
// Class operations
private static _globalInstance;
public static CreateGlobalInstance(RepositoryFactory factory)
{
_glocalInstance = factory;
}
public static I Get<I>()
{
return _globalInstance.Get<I>();
}
/////////////////////
///// this operation should be inherited by:
///// * NHibernateRepositoryFactory //
///// * Linq2SqlRepositoryFactory ////
///// * NUnitRepositoryFactory ///////
///// it depends in your context ////////////////////////
public abstract I GetRepository<I>();
}
Lo he estado haciendo durante años sin ningún problema en mis pruebas unitarias.
Por lo tanto, la inyección de dependencia solo se requiere en esta clase RepositoryFactory.
Me temo que has malinterpretado un poco la intención del patrón Repositorio.
El Depósito está destinado a comportarse como una colección en memoria de un objeto de dominio particular, generalmente una raíz agregada:
interface EmployeeRepository
{
List<Employee> retrieveBy(Criteria someCriteria);
void store(Employee someEmployee);
int countOf(Criteria someCriteria);
// convenience methods
Employee retrieveById(String id);
Employee retrieveBySSN(String ssn);
}
Los clientes de este código no tienen idea de si la colección está en memoria, como lo haría para pruebas unitarias, o hablando con un asignador ORM en algunos casos, o llamando a un procedimiento almacenado en otros, o manteniendo un caché para ciertos objetos de dominio .
Esto todavía no responde su pregunta. De hecho, es posible que los objetos de dominio tengan métodos save () y load () que se delegan en el repositorio correcto. No creo que este sea el camino correcto porque la persistencia casi nunca forma parte del dominio comercial, y le da a su objeto de dominio más de un motivo para cambiar.
Echa un vistazo a esta pregunta relacionada para más divagaciones.
En respuesta a algunos comentarios sobre esta respuesta:
Una crítica válida. Sin embargo, todavía estoy confundido sobre cómo obtener un único objeto de dominio o una colección de objetos de dominio relacionados cuando se está dentro del contexto de un objeto de dominio existente. - gabriel1836
Digamos que un empleado tiene muchas habilidades. No veo nada de malo en que el Repositorio de Empleados llame a un Repositorio de habilidades como este:
// assume EmployeeRepository talks to a DB via sprocs
public Employee retrieveById(String id)
{
ResultSet employeeResultSet = this.database.callSproc("PROC_GetEmployeeById",
new Object[] { id });
List<Skill> skills =
new SkillRepository().retrieveBy(new EqualsCriteria("EmployeeId", id));
Employee reconstructed = new EmployeeFactory().createNew().
fromResultSet(employeeResultSet).
withSkills(skills).
build();
return reconstructed;
}
Otra ruta es llamar al Repositorio de Habilidades, hacer que el Repositorio de Empleados llame, en este ejemplo, al procedimiento almacenado para cargar el conjunto de resultados de habilidades, luego delegarlo a una Fábrica de Habilidades para obtener la lista de Habilidades.
¿No puedo hacer una llamada al Repositorio y si emite una llamada al mapeador de datos o carga el objeto en la memoria es su preocupación, ¿no? - gabriel1836
Exactamente correcto. Normalmente me burlo de todo el nivel de datos en mi unidad de pruebas de esta manera.