Menu

Repository Framework

Daniel P. Dougherty

Disclaimer:
You don't need to use any classes discussed here. While it's not at all necessary to use any classes from the Repository Framework, the Snifflib library does provide for you ready-made long-term persistence solutions for many of its core classes (e.g. DblMatrix) through the Repository Framework.

You should consider using the Repository Framework if you...

  • desire a pure JAVA long-term persistence solution
  • want to focus on getting up-and-running
  • want to allow for gradual adoption of fancier back-ends (e.g. host a remote SQL database)
  • want coherent interface specification towards which to develop your code
  • want to customize your own persistence methods like set/get or search/query for your own usage cases
  • don't want/need existing persistence frameworks (e.g. JAVABeans, exhaustive serialization etc.)
  • want to bring long-term persistence capabilities to an existing library
  • don't want persistence concerns to excessively dominate other design choices
  • want to allow transfer of storage between various back-ends
    • (Local XML)-to-(Remote MySQL)
    • (Remote MySQL)-to-(Local SQLite)
    • (Local XML)-to-(Local DOM)
    • etc.

Introduction

The repository framework is a programming paradigm (pattern) focused on accessing distributed and possible asynchronous (transient) data. It provides an alternate approach to long-term persistence which differs from exhaustive serialization via the JavaBeans paradigm and is easily prototyped for different back-end storage solutions (a.k.a. repositories). The storage classes implementing the repository framework are plain JAVA objects whose class methods can be overridden in the standard way to optimize the efficiency of the chosen back-end. Such optimizations might included taking advantage of SQL Joins, SQL indexing, or random access of XML files from disk. The developer is free at a later time to go back to fine-tune class method implementations to suit their application's or client's needs. Note that many such optimizations are done server-side (e.g. SQL indexing) and so shouldn't require modifying the implementation and even less so the interface definition. The goal is to prototype first, optimize later.

A key design goal of the repository framework is to allow a developer to get up and running with basic prototypes on their local machine first without needing to, for example, set up dedicated SQL servers for their application. By focusing first on design, programming to interfaces, and reaching consensus about what instance information actually needs to be persisted faster prototyping can be realized.

Then, once their project's interface definition begins to take form, and perhaps a few GUI prototypes developed too, developers may choose to additionally allow for distributed remote storage solutions by implementing extensions of RepositoryStorage. Snifflib already supplies default prototypes for the following

Reposconfig Package

The core repository framework classes and interfaces are located in the reposconfig package. A developer using the Snifflib library can easily extend, override, and otherwise enhance and customize these core classes and interfaces to suit specific needs. However, for many common uses the default implementations provided (DOM and XML interfaces in particular) should allow programmers to get some working prototypes up and running on their local machine.

Configs

Connectivity to any such back-end is configured through a class implementing the RepositoryConfiguration interface. The Snifflib library already implements this class for many commonly usage cases. These include,

Generically, we will refer to any such a class providing connectivity to a back-end as a Config.

The RepositoryStorage Interface

Any storage classes will need to implement the RepositoryStorage interface which provides a minimal set of methods relating to provenance as well as commenting on the Five W's of a particular storage instance.

Storage classes must extend the RepositoryStorage interface to provide the information essential for persisting a storage instance. For example, the DblMatrixStorage class provides additional methods for persisting an instance of the DblMatrix class. The method getMatrixSize() returns the dimensions of the possibly high-dimensional matrix which a back-end can use to determine how much memory or storage to allocate. DblMatrixStorage also specifies convenience methods like copyFromDblMatrix(). This method overwrites an existing persisted storage's contents with the contents of a runtime instance of the DblMatrix class.

When to Delegate to a TransferAgent

Depending on the circumstances, you might want to use a delegation pattern in your storage classes. In fact, a higher level method like copyFromDblMatrix() may only need to call other lower-level methods already specified in the relevant RepositoryStorage interface (i.e. DblMatrixStorage). Since the storage interfaces for each specific back-end (e.g. DblMatrixDOM, DblMatrixXML,DblMatrixPostgreSQL) will also need to implement this method, it may make sense to delegate method calls in each storage class to a generic class implementing this method. We will call such a generic workhorse a TransferAgent.

For our current example, the class DblMatrixTransferAgent implements the method copyFromDblMatrix() using only methods specified in DblMatrixStorage. Calls to the copyFromDblMatrix() method of DblMatrixDOM, DblMatrixXML, or DblMatrixPostgreSQL are delegated to the same copyFromDblMatrix() implemented in DblMatrixTransferAgent. The code in the generic implementation ensures the incumbent low-level calls are actually carried out by the appropriate target storage class instance's methods.


Related

Wiki: Home
Wiki: Your First Repository Framework App

Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.