38.10. Mapping Objects: Database Mapper or Database Broker Pattern
The PersistenceFacade as true of all facadesdoes not do the work itself, but delegates requests to subsystem objects.Who should be responsible for materialization and dematerialization of objects (for example, a ProductDescription ) from a persistent store?The Information Expert pattern suggests that the persistent object class itself (ProductDescription ) is a candidate, because it has some of the data (the data to be saved) required by the responsibility.If a persistent object class defines the code to save itself in a database, it is called a direct mapping design. Direct mapping is workable if the database related code is automatically generated and injected into the class by a post-processing compiler, and the developer never has to see or maintain this complex database code cluttering his or her class.But if direct mapping is manually added and maintained, it has a number of defects and does not tend to scale well in terms of programming and maintenance. Problems include:
- Strong coupling of the persistent object class to persistent storage knowledgeviolation of Low Coupling.
- Complex responsibilities in a new and unrelated area to what the object was previously responsible forviolation of High Cohesion and maintaining a separation of concerns. Technical service concerns are mixing with application logic concerns.
We will explore a classic indirect mapping approach, that uses other objects to do the mapping for persistent objects.Part of this approach is to use the Database Broker pattern [BW95]. It proposes making a class that is responsible for materialization, dematerialization, and object caching. This has also been called the Database Mapper pattern in [Fowler01], which is a better name than Database Broker, as it describes its responsibility, and the term "broker" in distributed systems [BMRSS96] design has a long-standing and different meaning.[1]
[1] In distributed systems, a broker is a front-end server process that delegates tasks to back-end server processes.
A different mapper class is defined for each persistent object class. Figure 38.5 illustrates that each persistent object may have its own mapper class, and that there may be different kinds of mappers for different storage mechanisms. A snippet of code:
class PersistenceFacade
{
// ...
public Object get( OID oid, Class persistenceClass )
{
// an IMapper is keyed by the Class of the persistent object
IMapper mapper = (IMapper) mappers.get( persistenceClass );
// delegate
return mapper.get( oid );
}
//...
}
Figure 38.5. Database Mappers.
[View full size image]
Metadata-Based Mappers
More flexible, but more involved, is a mapper design based on metadata (data about data). In contrast to hand-crafting individual mapper classes for different persistent types, metadata-based mappers dynamically generate the mapping from an object schema to another schema (such as relational) based on reading in metadata that describes the mapping, such as "TableX maps to Class Y; column Z maps to object property P" (it gets much more complex). This approach is feasible for languages with reflective programming capabilities, such as Java, C#, or Smalltalk, and awkward for those that don't, such as C++.