10

我最初是按照这篇 codeproject 文章中概述的 s# 架构示例来设计我的系统的(不幸的是,我没有使用 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();
}

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

4

5 回答 5

3

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

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

这听起来需要付出很多努力,但除了自定义查询之外,至少如果您使用现代 ORM,大多数代码的实现非常简单,因此我使用继承(ReadWriteRepositoryBase where T: IAggregateRoot)和/或组合(调用到 RepositoryHelper 类)。基类可能具有适用于所有情况的方法,例如 GetById。

希望这可以帮助。

于 2008-10-25T16:46:10.827 回答
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

于 2008-09-27T21:41:17.097 回答
2

乔治 我很清楚你的感受。Billy 的架构对我来说很有意义,但是创建容器、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。

于 2008-09-29T12:04:58.183 回答
1

我还在为我的 DAO 使用存储库模式,对此我很满意。是的,你最终得到了很多小类,但它非常易于维护。如果您使用接口(LINQ),它会更强大。如果您有一个非常一致的数据库结构,IQueriable您也可以使用泛型(类似的东西)。T GetById<T>(int id)

于 2008-09-27T21:40:36.070 回答
0

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

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

于 2008-09-29T12:12:00.243 回答