数据访问层应如何构造?

时间:2020-03-06 14:49:56  来源:igfitidea点击:

我最初是根据此代码项目文章中概述的体系结构示例设计系统的(不幸的是,我没有使用NHibernate)。基本思想是,对于每个需要与持久层进行通信的域对象,我们将在不同的库中拥有一个相应的数据访问对象。每个数据访问对象都实现一个接口,并且当域对象需要访问数据访问方法时,它总是根据接口而不是DAO本身进行编码。

在当时和现在,我仍然认为这种设计非常灵活。但是,随着域模型中对象数量的增加,我发现自己正在质疑这里是否没有组织问题。例如,域中几乎每个对象最终都有一个对应的数据访问对象和数据访问对象接口。不仅如此,而且它们每个都位于不同的地方,如果我想做一些简单的事情(如在某些名称空间周围移动),则很难维护。

有趣的是,这些DAO(及其对应的接口)中的许多都是非常简单的生物,最常见的生物只有一个GetById()方法。我最终得到了一堆物体,例如

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

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

他们的实现者通常也很琐碎。这让我想知道,朝着不同的方向前进会不会更简单,也许是通过为一个简单的数据访问任务使用一个对象来切换我的策略,并为那些需要更多信息的对象保留专用的数据访问对象的创建复杂。

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

有人对这样的架构有任何经验吗?总体而言,我对设置感到非常满意,因为现在我唯一关心的就是管理所有这些小文件。但是,我仍然想知道还有什么其他方法来构造数据访问层。

解决方案

我还在DAO中使用存储库模式,对此我感到非常满意。是的,我们最终会遇到很多小类,但是它是非常可维护的。如果使用IQueriable接口(LINQ),它的功能会更强大。如果我们具有相当一致的数据库结构,则也可以使用泛型(类似于T GetById <T>(int id)之类的东西)。

我使用PHP工作,但是我为数据访问层设置了类似的设置。我已经实现了一个看起来像这样的接口:

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

然后,我的每个数据访问对象的工作方式如下:

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        
}

我当前正在手动构建每个数据访问对象类,因此当我添加表或者修改数据库中的现有表时,显然我必须手动编写新代码。就我而言,这与我们的代码库相比仍然是一个巨大的进步。

但是,我们也可以使用SQL元数据(假设我们已经设计了相当合理的数据库设计,利用了外键约束等)来生成这些数据访问对象。然后,从理论上讲,我们可以使用单个父DataAccessObject类构造该类的属性和方法,甚至自动建立与数据库中其他表的关系。这将或者多或者少地完成与我们描述的相同的事情,因为我们可以扩展DataAccessObject类以为需要大量手动构建代码的情况提供自定义方法和属性。

作为.NET开发的补充说明,我们是否看过一个框架,该框架可以为我们处理数据访问层的基础结构,例如Subsonic?如果没有,我建议我们研究一下这样的框架:http://subsonicproject.com/。

或者对于PHP开发,诸如Zend Framework之类的框架将提供类似的功能:http://framework.zend.com

乔治,我确切地知道你的感觉。 Billy的体系结构对我来说很有意义,但是创建一个容器,Imapper和mapper文件的需要非常痛苦。然后,如果我们使用的是NHibernate相应的.hbm文件,通常使用一些单元测试脚本来检查所有功能。

我假设即使我们没有使用NHibernate,我们仍然使用通用基类来加载/保存容器,即

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

我想如果没有NHibernate,我们将使用反射或者其他机制来确定要调用的SQL / SPROC?

无论如何,我对此的想法是,DAO只需要执行在基类中定义的基本CRUD操作,然后就不需要编写自定义映射器和接口。我能想到的唯一方法是使用Reflection.Emit即时动态创建DAO。

第二种方法的主要缺点是在单元测试中-与仅模拟ICustomerDao相比,模拟出诸如SimpleObjectRepository之类的大型工厂方法需要更多的工作。但是,我的项目采用了针对高度相关对象的第二种方法(其中大多数将在任何单元测试中使用),因为它减轻了精神负担并使其更易于维护。

我会弄清楚是什么使系统更易于维护和理解,并做到这一点。

我建议不要使用简单方法,而不是在简单系统中,通常我认为最好为每个聚合创建一个自定义存储库,并在其中尽可能多地封装适当的逻辑。

因此,我的方法是为每个需要它的集合提供一个存储库,例如CustomerRepository。这将具有Add(保存)方法,并且如果适用于该聚合,则将具有Remove(删除)方法。它还将具有任何其他适用的自定义方法,包括查询(GetActive),也许其中一些查询可以接受规范。

这听起来很费力,但是除了自定义查询外,大多数代码(至少在使用现代ORM的情况下)实现起来非常简单,因此我使用继承(ReadWriteRepositoryBase,其中T:IAggregateRoot)和/或者组合(调用移至RepositoryHelper类)。基类可能具有适用于所有情况的方法,例如GetById。

希望这可以帮助。