Reputation: 11502
I am writing a new website project pretty much from scratch. The project is in C# but my background is in PHP (the pseudo-code below is a bit if a mixture, trying to be both succinct and declarative).
The problem
I need to retrieve configuration data from one of two places - sometimes from the local Database and sometimes from a Soap service. I should be able to create the same set of Model objects from either source.
The data is stored in completely different structures in the different data sources - several different Soap responses need to be pieced together from the Soap end, whereas the DB structure is much closer to how I'm structuring my Model objects in the code.
The configuration is made up of a tree of objects: Products contain Properties which contain Options which have Conditions for when they apply.
The goals
I want to separate concerns as much as possible (to hopefully facilitate both test-/maintain-/extend-ability):
Questions
I'm aware of various design patterns around this (although I'm not entirely sure I understand them entirely). I asked a question on Programmers similar to this and got a response about Persistence Ignorance (more here) and the Repository pattern, which both seem to be concepts from the Microsoft world.
As far as I can tell "Persistence Ignorance" is simply the concept of having Model objects that know nothing about your Data Storage mechanism, and the Repository pattern seems very similar to the Data Mapper pattern, except that it may be more of a facade, hiding more of what's actually going on.
So my questions are:
In the Data Mapper pattern should I have one Mapper per Model object? Rather than having one for the entire configuration tree?
And therefore, should I have a configuration tree building object that uses all those Mapper objects?
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) { /* ... */ }
}
Does this seem like a good solution?
Where should the logic to build the objects be located?
At some point there needs to be a considerable amount of logic to build my configuration objects from the random array of XML data that I get back from the Soap service, and a smaller amount of logic to do the same from the database.
Should I be putting the logic to build the objects in separate instances of the Mapper objects?
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 */ }
}
Is this okay? Should this logic be abstracted further? If so why? Also, I'm a bit uneasy about the ProductMapperXml
object having considerable logic itself and also being responsible for creating Product
objects internally. Should I be passing it a ProductFactory
of some sort? Or just use a factory method?
Please please let me know if there are more elegant ways to solve this than my suggestions? Also if there are any layers of abstraction or Design Patterns I could benefit from that I've missed?
Upvotes: 5
Views: 3110
Reputation: 11502
Since no answers are forthcoming I'm going to write this up myself.
I did end up going with the DataMapper
pattern - I hope I implemented it in a sensible fashion.
I didn't end up using any Factory class, because I was able to build the tree through the classes themselves.
I created a bunch of Mapper interfaces:
interface Models.Interfaces.IModel {}
interface Mappers.Interfaces.IMapper {}
interface Mappers.Interfaces.IDatabaseMapper : IMapper {}
interface Mappers.Interfaces.IXmlMapper : IMapper {}
I created Models for everything:
class Models.Product : IModel {}
class Models.Property : IModel {}
class Models.Option : IModel {}
class Models.Condition : IModel {}
And then I gave each model two mapper classes:
class Mappers.ProductDatabaseMapper : IDatabaseMapper {}
class Mappers.ProductXmlMapper : IXmlMapper {}
/* ... (same for each model) */
Each Mapper class has methods to create its children:
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;
}
}
I think this presents a fairly neat solution. Although I'm still a little worried about the fact that my various Mapper
classes are creating objects themselves. But I think I'll only separate that out into a factory as and when it becomes necessary.
Upvotes: 3