如何为不同的数据源实现Data Mapper/Repository持久性模式?

Rob*_*low 5 c# model-view-controller design-patterns data-access-layer datamapper

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

问题

我需要从两个地方之一检索配置数据 - 有时从本地数据库,有时从Soap服务.我应该能够从任一源创建同一组Model对象.

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

配置由对象树组成:产品包含属性,其中包含选项,这些选项具有应用时间的条件.

目标

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

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

问题

我知道各种各样的设计模式(虽然我不完全确定我完全理解它们).我向程序员提出了一个与此类似的问题,得到了关于Persistence Ignorance(更多信息)和Repository模式的回复,它们似乎都是来自Microsoft世界的概念.

据我所知,"Persistence Ignorance"只是让Model对象对你的数据存储机制一无所知的概念,而Repository模式看起来与Data Mapper模式非常相似,只不过它可能更像是一个外观,隐藏更多实际发生的事情.

所以我的问题是:

在Data Mapper模式中,每个Model对象应该有一个Mapper吗?而不是一个整个配置树?

因此,我应该有一个使用所有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) { /* ... */ }
}
Run Code Online (Sandbox Code Playgroud)

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

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

在某些时候,需要有大量的逻辑来从我从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 */ }
}
Run Code Online (Sandbox Code Playgroud)

这个可以吗?这个逻辑应该进一步抽象吗?如果是这样的话?此外,我对这个ProductMapperXml具有相当逻辑的对象有点不安,并且还负责在Product内部创建对象.我应该通过ProductFactory某种方式吗?或者只是使用工厂方法?

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

Rob*_*low 3

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

我最终确实采用了这种DataMapper模式——我希望我以一种明智的方式实现它。

我最终没有使用任何工厂类,因为我能够通过类本身构建树。

我创建了一堆Mapper接口:

interface Models.Interfaces.IModel {}
interface Mappers.Interfaces.IMapper {}
interface Mappers.Interfaces.IDatabaseMapper : IMapper {}
interface Mappers.Interfaces.IXmlMapper : IMapper {}
Run Code Online (Sandbox Code Playgroud)

我为一切创建了模型:

class Models.Product : IModel {}
class Models.Property : IModel {}
class Models.Option : IModel {}
class Models.Condition : IModel {}
Run Code Online (Sandbox Code Playgroud)

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

class Mappers.ProductDatabaseMapper : IDatabaseMapper {}
class Mappers.ProductXmlMapper : IXmlMapper {}
/* ... (same for each model) */
Run Code Online (Sandbox Code Playgroud)

每个 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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