Pregunta

Inicialmente diseñé mi sistema siguiendo el ejemplo de arquitectura s # descrito en este artículo del proyecto de código (Desafortunadamente, no estoy usando NHibernate). La idea básica es que para cada objeto de dominio que necesitaría comunicarse con la capa de persistencia, tendría un objeto de acceso a datos correspondiente en una biblioteca diferente. Cada objeto de acceso a datos implementa una interfaz y cuando un objeto de dominio necesita acceder a un método de acceso a datos, siempre codifica contra una interfaz y nunca contra los propios DAO.

En ese momento, y aún así, pensé que este diseño era muy flexible. Sin embargo, a medida que la cantidad de objetos en mi modelo de dominio ha crecido, me pregunto si no hay un problema de organización aquí. Por ejemplo, casi todos los objetos en el dominio terminan con un objeto de acceso a datos y una interfaz de objeto de acceso a datos correspondientes. No solo eso, sino que cada uno de estos está en un lugar diferente que es más difícil de mantener si quiero hacer algo simple como cambiar algunos espacios de nombres.

Curiosamente, muchos de estos DAO (y sus interfaces correspondientes) son criaturas muy simples: la más común tiene un solo método GetById (). Termino con un montón de objetos como

public interface ICustomerDao {
  Customer GetById(int id);
}

public interface IProductDao {
  Product GetById(int id);
}
public interface IAutomaticWeaselDao {
  AutomaticWeasel GetById(int id);
}

Donde sus implementadores también suelen ser muy triviales. Esto me hace preguntarme si no sería más sencillo ir en una dirección diferente, tal vez cambiar mi estrategia al tener un solo objeto para tareas simples de acceso a datos y reservar la creación de objetos de acceso a datos dedicados para aquellos que necesitan algo un poco más complicado.

public interface SimpleObjectRepository {
      Customer GetCustomerById(int id);
      Product GetProductById(int id);
      AutomaticWeasel GetAutomaticWeaselById(int id);
      Transaction GetTransactioinById(int id);
}
public interface TransactionDao {
  Transaction[] GetAllCurrentlyOngoingTransactionsInitiatedByASweatyGuyNamedCarl();
}

¿Alguien tiene alguna experiencia con una arquitectura como esta? En general, estoy muy contento con la configuración, ya que ahora mi única preocupación es la administración de todos estos pequeños archivos. Sin embargo, todavía me pregunto qué otros enfoques existen para estructurar la capa de acceso a datos.

¿Fue útil?

Solución

Recomiendo contra el enfoque simple que no sea en sistemas simples, generalmente creo que es mejor crear un repositorio personalizado para cada agregado y encapsular tanta lógica adecuada como sea posible dentro de él.

Por lo tanto, mi enfoque sería tener un repositorio para cada agregado que lo necesite, como CustomerRepository. Esto tendría un método Agregar (guardar) y, si es adecuado para ese agregado, un método Eliminar (eliminar). También tendría otros métodos personalizados que se aplican, incluidas las consultas (GetActive) y tal vez algunas de esas consultas podrían aceptar especificaciones.

Esto parece mucho esfuerzo, pero aparte de las consultas personalizadas, la mayor parte del código es, al menos si está utilizando un ORM moderno, muy simple de implementar, por lo que uso la herencia (ReadWriteRepositoryBase donde T: IAggregateRoot) y / o composición (llamando a una clase RepositoryHelper). La clase base puede tener métodos que se aplican en todos los casos, como GetById.

Espero que esto ayude.

Otros consejos

Trabajo en PHP, pero tengo algo similar configurado para mi capa de acceso a datos. He implementado una interfaz que se parece a esto:

interface DataAccessObject
{
public static function get(array $filters = array(), array $order = array(), array $limit = array());
public function insert();
public function update();   
public function delete();
}

Y luego, cada uno de mis objetos de acceso a datos funciona de la siguiente manera:

class DataAccessObject implements DataAccessObject
{
    public function __construct($dao_id = null) // So you can construct an empty object
    {
        // Some Code that get the values from the database and assigns them as properties
    }
    public static function get(array $filters = array(), array $order = array(), array $limit = array()) {};  // Code to implement function
    public function insert() {};  // Code to implement function
    public function update() {};  // Code to implement function 
    public function delete() {};  // Code to implement function        
}

Actualmente estoy construyendo cada una de las clases de objetos de acceso a datos manualmente, de modo que cuando agrego una tabla o modifico una tabla existente en la base de datos, obviamente tengo que escribir el nuevo código a mano. En mi caso, esto sigue siendo un gran paso desde donde estaba nuestra base de código.

Sin embargo, también puede usar los metadatos de SQL (suponiendo que tenga un diseño de base de datos bastante sólido que aproveche las restricciones de clave externa y similares) para generar estos objetos de acceso a datos. Luego, en teoría, podría usar una clase DataAccessObject padre única para construir las propiedades y métodos de la clase e incluso construir relaciones con las otras tablas de la base de datos automáticamente. Esto lograría más o menos lo mismo que está describiendo porque podría extender la clase DataAccessObject para proporcionar métodos y propiedades personalizados para situaciones que requieren cierta cantidad de código construido manualmente.

Como nota al margen para el desarrollo de .NET, ¿ha analizado un marco que maneje la estructura subyacente de la capa de acceso a datos, como Subsonic? Si no es así, recomendaría buscar un marco de este tipo: http://subsonicproject.com/ .

O para el desarrollo de PHP, un marco como Zend Framework proporcionaría una funcionalidad similar: http://framework.zend.com

George, sé exactamente cómo te sientes. La arquitectura de Billy tiene sentido para mí, pero la necesidad de crear un contenedor, Imapper y archivos de mapeo son dolorosos. Luego, si está usando NHibernate, el archivo .hbm con corrosión y, por lo general, algunas secuencias de comandos de prueba unitaria para verificar que todo funcione.

Supongo que aunque no esté usando NHibernate, todavía está usando una clase base genérica para cargar / guardar sus contenedores, es decir

public class BaseDAO<T> : IDAO<T> 
{
     public T Save(T entity)
     { 
       //etc......
     }
}
public class YourDAO : BaseDAO<YourEntity>
{
}

Supongo que sin NHibernate estaría utilizando la reflexión o algún otro mecanismo para determinar a qué SQL / SPROC llamar?

De todos modos, mi pensamiento sobre esto sería donde DAO solo necesita realizar las operaciones CRUD básicas definidas en la clase base, entonces no debería haber necesidad de escribir mapeadores e interfaces personalizados. La única forma en que puedo pensar en lograr esto es usar Reflection.Emit para crear dinámicamente tu DAO sobre la marcha.

También estoy usando el patrón de repositorio para mi DAO y estoy bastante contento con eso. Sí, terminas con unas pocas clases pequeñas, pero es muy fácil de mantener. Es un poco más poderoso si usa la interfaz IQueriable (LINQ.) También podría usar genéricos (algo así como T GetById<T>(int id)) si tiene una estructura de base de datos bastante consistente.

El principal inconveniente del segundo enfoque está en las pruebas unitarias: burlarse de los grandes métodos de fábrica como su SimpleObjectRepository requiere más trabajo que burlarse solo de ICustomerDao. Pero, mi proyecto va con el segundo enfoque para objetos altamente relacionados (donde la mayoría se usará en cualquier prueba unitaria) porque aligera la carga mental y la hace más fácil de mantener.

Descubriría qué hace que su sistema sea más fácil de mantener y fácil de entender y hacer eso.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top