hashtags architecture data-access-layer

architecture - hashtags - ¿Cómo debería estructurarse la capa de acceso a los datos?



architecture hashtags instagram (5)

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

Inicialmente diseñé mi sistema siguiendo el ejemplo de arquitectura de s # descrito en este artículo de 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 acceso 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 estoy cuestionando si no hay un problema de organización aquí. Por ejemplo, casi todos los objetos del dominio terminan con una interfaz correspondiente de objeto de acceso a datos y objetos de acceso a datos. No solo eso, sino que cada uno de ellos está en un lugar diferente, que es más difícil de mantener si quiero hacer algo simple como desplazarme por algunos espacios de nombres.

Curiosamente, muchos de estos DAO (y sus interfaces correspondientes) son criaturas muy simples, el 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 son generalmente muy triviales también. 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 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 es mi única preocupación la gestión de todos estos pequeños archivos. Todavía me pregunto, sin embargo, qué otros enfoques existen para estructurar la capa de acceso a datos.


Trabajo en PHP, pero tengo algo similar configurado para mi capa de acceso a datos. Implementé una interfaz que se ve así:

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 compilando cada una de las clases de objetos de acceso a datos manualmente, así 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 adelante desde donde estaba nuestra base de códigos.

Sin embargo, también puede usar los metadatos de SQL (suponiendo que tiene 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 principal DataAccessObject para construir las propiedades y métodos de la clase e incluso construir relaciones con las otras tablas en la base de datos automáticamente. Esto lograría más o menos lo mismo que está describiendo porque entonces 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 los datos para usted, como Subsonic? De lo contrario, recomendaría buscar en ese marco: http://subsonicproject.com/ .

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


El principal inconveniente del segundo enfoque es la prueba unitaria: burlarse de los grandes métodos de fábrica como SimpleObjectRepository requiere más trabajo que burlarse de ICustomerDao. Pero, mi proyecto va con el segundo enfoque para objetos altamente relacionados (donde la mayoría se utilizará en cualquier prueba de unidad) 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.


George, sé exactamente cómo te sientes. La arquitectura de Billy tiene sentido para mí, pero la necesidad de crear un contenedor, Imapper y los archivos de mapeo son dolorosos. Luego, si está usando NHibernate, el archivo corrosponding .hbm y, por lo general, algunos scripts de prueba unitarios para verificar todo lo que funciona.

Supongo que, aunque no estés usando NHibernate, sigues usando una clase base genérica para cargar / guardar tus 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ías usando la reflexión o algún otro mecanismo para determinar a qué SQL / SPROC llamar?

Eitherway, 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 personalizados e interfaces. La única forma en que puedo pensar en lograr esto es usar Reflection.Emit para crear dinámicamente tu DAO sobre la marcha.


Recomiendo el enfoque simple que no sea en sistemas simples, por lo general creo que es mejor crear un repositorio personalizado para cada agregado y encapsular la lógica más adecuada que se pueda dentro de él.

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

Esto suena como un gran esfuerzo, pero aparte de las consultas personalizadas, la mayoría del código es, al menos si está utilizando un ORM moderno, es muy fácil de implementar, así que utilizo la herencia (ReadWriteRepositoryBase donde T: IAggregateRoot) y / o composición (llamadas a una clase RepositoryHelper). La clase base puede tener métodos que se apliquen en todos los casos, como GetById.

Espero que esto ayude.