数据访问层应该如何构建?

时间:2008-09-27 21:16:55

标签: architecture data-access-layer

我最初按照s#架构示例outlined in this codeproject article设计我的系统(不幸的是,我没有使用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();
}

有没有人对这样的架构有任何经验?总的来说,我对设置非常满意,因为现在我唯一担心的是管理所有这些小文件。我仍然想知道存在什么其他方法来构建数据访问层。

5 个答案:

答案 0 :(得分:3)

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

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

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

希望这有帮助。

答案 1 :(得分:2)

我在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

答案 2 :(得分:2)

乔治我确切地知道你的感受。比利的架构对我来说很有意义,但创建容器,Imapper和映射器文件的需求是痛苦的。然后,如果你使用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。

答案 3 :(得分:1)

我也使用repository pattern作为我的DAO,我对它很满意。是的,你最终得到了不少小班,但它非常易于维护。如果你使用IQueriable接口(LINQ。)它会更强大。如果你有一个非常一致的数据库结构,你也可以使用泛型(类似于T GetById<T>(int id))。

答案 4 :(得分:0)

第二种方法的主要缺点是单元测试 - 模拟像SimpleObjectRepository这样的大型工厂方法需要比模拟ICustomerDao更多的工作。但是,我的项目采用第二种方法处理高度相关的对象(其中大多数将用于任何单元测试),因为它减轻了心理负荷并使其更易于维护。

我会弄清楚是什么让你的系统更易于维护和理解,并且可以做到这一点。