如何为不同的数据源实现数据映射器/存储库持久性模式?

Posted

技术标签:

【中文标题】如何为不同的数据源实现数据映射器/存储库持久性模式?【英文标题】:How to implement Data Mapper / Repository persistence patterns for diverse data sources? 【发布时间】:2012-10-08 20:27:54 【问题描述】:

我正在从头开始编写一个新的网站项目。该项目是 C#,但我的背景是 php(下面的伪代码有点混合,试图既简洁又具有声明性)。

问题

我需要从两个地方之一检索配置数据 - 有时来自本地数据库,有时来自 Soap 服务。我应该能够从任一来源创建相同的模型对象集。

数据以完全不同的结构存储在不同的数据源中——几个不同的 Soap 响应需要从 Soap 端拼凑起来,而 DB 结构更接近于我在代码中构建模型对象的方式.

配置由对象树组成:Products 包含 Properties 包含 OptionsConditions 何时申请。

目标

我想尽可能地分离关注点(希望促进测试/维护/扩展能力):

我的模型对象应该对数据持久性或检索一无所知 Data Persistence 对象应尽可能与数据源的确切类型无关 我想尽可能地将对象创建分开。

问题

我知道与此相关的各种设计模式(尽管我不完全确定我完全理解它们)。我asked a question on Programmers 与此类似,并得到了关于Persistence Ignorance (more here) 和Repository 模式的回复,这两者似乎都是来自微软世界的概念。

据我所知,“持久性无知”只是让模型对象对您的数据存储机制一无所知的概念,并且存储库模式似乎与数据映射器模式非常相似,除了 may be more of a facade,隐藏更多实际发生的事情。

所以我的问题是:

在数据映射器模式中,每个模型对象是否应该有一个映射器?而不是为整个配置树创建一个?

因此,我是否应该有一个使用所有这些 Mapper 对象的配置树构建对象?

class ConfigBuilder() 
    public ConfigBuilder (productMapper, propertyMapper, optionMapper, conditionMapper) 
        // save them into local properties
    

    public Products() 
       var products = productMapper.FetchAll();

       foreach (var product in products) 
           AddProperties(product);
       

        return products;
    

    private AddProperties(products)  /* ... */ 
    private AddOptions(property)  /* ... */ 
    private AddConditions(option)  /* ... */ 

这似乎是一个好的解决方案?

构建对象的逻辑应该放在哪里?

在某些时候,需要大量的逻辑来从我从 Soap 服务返回的 XML 数据的随机数组构建我的配置对象,并且需要少量的逻辑来从数据库中执行相同的操作。

我应该将构建对象的逻辑放在 Mapper 对象的单独实例中吗?

interface IProductMapper  FetchAll; FetchByCode; Create; Delete; Update  

class ProductMapperXml implements IProductMapper 
    public ProductMapperXml(xmlDataSource) 
    public FetchAll()  /* A whole bunch of logic to create the Product objects from XML data source */ 


class ProductMapperDatabase implements IProductMapper 
    public ProductMapperDatabase(databaseConnection) 
    public FetchAll()  /* Select and build products from the database */ 

这样好吗?是否应该进一步抽象这个逻辑?如果是,为什么?另外,我对ProductMapperXml 对象本身具有相当大的逻辑并且还负责在内部创建Product 对象感到有些不安。我应该传递某种ProductFactory 吗?还是只使用工厂方法?

如果有比我的建议更优雅的方法来解决这个问题,请告诉我?另外,如果我错过了任何抽象层或设计模式,我可以从中受益吗?

【问题讨论】:

【参考方案1】:

由于没有答案,我将自己写下来。

我最终选择了DataMapper 模式——我希望我以一种明智的方式实现它。

我最终没有使用任何 Factory 类,因为我能够通过这些类自己构建树。

我创建了一堆 Mapper 接口:

interface Models.Interfaces.IModel 
interface Mappers.Interfaces.IMapper 
interface Mappers.Interfaces.IDatabaseMapper : IMapper 
interface Mappers.Interfaces.IXmlMapper : IMapper 

我为所有东西创建了模型:

class Models.Product : IModel 
class Models.Property : IModel 
class Models.Option : IModel 
class Models.Condition : IModel 

然后我给每个模型两个映射器类:

class Mappers.ProductDatabaseMapper : IDatabaseMapper 
class Mappers.ProductXmlMapper : IXmlMapper 
/* ... (same for each model) */

每个 Mapper 类都有创建子类的方法:

class ProductDatabaseMapper :IDatabaseMapper 
    public List<Product> FetchAllWithChildren 
        var productRows = DbManager.FetchAll("Product");
        var products = List<Product>();

        foreach(var productRow in productRows) 
            var product = new Product(productRow["Name"]);
            product.Properties = PropertyManagerInstance.FetchAllWithChildren(product);
            products.Add(product):
        

        return products;
    

我认为这是一个相当巧妙的解决方案。尽管我仍然有点担心我的各种Mapper 类正在自己创建对象。但我认为我只会在必要时将其分离到工厂中。

【讨论】:

以上是关于如何为不同的数据源实现数据映射器/存储库持久性模式?的主要内容,如果未能解决你的问题,请参考以下文章

可以自动更新数据库模式的 O/R 映射器?

Flask SQLAlchemy 数据映射器与活动记录模式

简单映射器模式 C# 代码生成模板

具有多个后端/DI 时的 ORM 和 POCO - 架构?自动映射器?

存储库和工作单元模式 - 如何保存更改

如何将不同的数据集传递给同一作业的两个不同的映射器