As I mentioned in the category on application partitioning, translation-based process have a rather different view of persistence than one encounters in the layered models for CRUD/USER processing where the application is essentially a pipeline between the RDB and the UI. In translation on views the UI, DB Access, and any hardware communications as low-level peer services to talk to the user, the database, or the hardware, respectively. Application partitioning also influences the way one thinks about these services and how one encapsulates them. In effect, these service subsystems are all peers at a fairly low-level of abstraction, each has its own subject matter complete with unique responsibilities, paradigms, and views, and each it isolated from the rest of the application through a pure data transfer interface.
Dealing with persistence is so ubiquitous in software development, particularly IT, that it is worth a category to discuss the overall approach to persistence that one should employ once one is outside the realm of CRUD/USER pipeline applications. (The RAD IDEs already provide a translation-based approach for that niche that hides most of the interesting stuff.)
The cardinal rule of application development outside the CRUD/USER arena is:
Solve the customer problem first and only then worry about what knowledge needs to be persisted and how it will be persisted.
The basic idea here is that we will encapsulate the specific persistence mechanisms in an application subsystem that is conventionally known as "DB Access". The interface to that subsystem will decouple the application solution from whatever the persistence mechanisms are -- schemas, stored procedures, "canned" joins, datasets, SQL, or whatever. Conceptually the problem solution sends messages to the DB Access subsystem like, "Here's a pile of data that I call X; store it," or "Give me that pile of data I gave you that I called X." The application solution will be responsible for extracting the pile of data from its objects and distributing data received from the DB to its objects. The DB Access subsystem is responsible for mapping the piles of data from the problem solution to the DB schemas or extracting data that the solution wants from the DB schemas.
Because we abstract from the problem solution only those problem space features that are relevant to the problem in hand and we typically tailor those abstractions in the process, it is likely that the problem solution will have a unique view that does not match 1:1 with, say, an enterprise RDB schema. This is especially true since an application problem solution with significant processing will tend to optimize its objects around behavior collaboration while the database is optimized around static data relationships. So we isolate the concerns of that mapping to the DB Access subsystem. Then when either the database view or the application solution views of the data change, the changes to the mapping will always be encapsulated in the DB Access subsystem.
A convenient characteristic of persistence paradigms is that they are very narrowly and rigorously defined. Whether it is a flat file, ad hoc direct access, RDB, or OODB, the fundamental paradigm in each case is defined with mathematical precision. That allows us to abstract the paradigm's invariants in a quite generic manner. That is, the DB Access subsystem's subject matter will likely have a very unique view of the data that is independent of particular applications. That is, it is focused on the persistence view rather than the problem solution view. This opens the opportunity for large scale reuse by deploying the same DB Access subsystem in multiple applications. To do so all we need is a mechanism for mapping the data identity ("X" in the example above) and message data packets in the
This is a very powerful form of reuse because it isolates in both directions. If the database changes, one can accommodate that change for all applications by modifying the DB Access subsystem that they all use. This is especially useful when one needs to resort to fancy optimizations (e.g., write caching) as database traffic increases. Similarly, when the application's data needs change, then all one needs to change is the parametric configuration data that maps the database view into the specific application view. Better yet, this can be done without touching the code of the DB Access subsystem. This leads to a very robust model for persistence access across all of one's applications.
In extreme cases, one can employ the subsystem interface model that I discussed in the application partitioning category to provide customized "glue" code in the application. That "glue" can be used when the interface the problem solution wants to "see" is very different from the interface the generic DB Access provides. That is, one can essentially employ a Facade design pattern to talk to the DB Access interface. The advantage here is that the "glue" code is isolated in a systematic way in a predicable place when it is necessary.
Naturally, there can be some big gaps between the proverbial cup and pitcher when one gets down to the actual practice. The next few posts in this category will hopefully provide some insight into how one actually realizes these advantages.