Functional structure
Fig. 1 illustrates a simplified data flow process in a data management application. Storage Library (StoreLib), shown in the center of the diagram, mediates data transfer between the user (UI) and storage (Database). The library contains two core class families, DataRecord and DataTable. DataRecord class family directly interacts with the UI, handles the data chunk currently presented to the user (e.g., a particular record), and can save a copy of this active chunk to a local file. DataTable interacts directly with the data storage and holds multiple chunks of data (e.g., a set of records).
Figure 1. Basic data flow diagram
Data management processes, including loading, editing, and saving the data via the StoreLib, are illustrated on the flow chart below.
A dedicated class, DataCompositeManager, bridges the two class families. It handles data transfers between DataRecord and DataTable without introducing unnecessary coupling.
Data model
DataRecordModel holds a single data record (table row). This model class has one backend, DataRecordWSheet, which implements the IDataRecordStorage interface, saves the data to Excel Worksheet, and populates the model at application startup. The DataRecordFactory abstract factory for record backends implements the IDataRecordFactory (Fig. 3).
Figure 3. DataRecord class diagram
DataTableModel contains a whole data table or a subset of rows, abstracting persistent storage. This model class has four backends, including DataTableWSheet, DataTableCSV, and DataTableADODB / DataTableSecureADODB. They handle a Worksheet, a delimited text file, and a relational database, respectively. The IDataTableStorage class formalizes their interface and the DataTableFactory/IDataTableStorage abstract factory instantiates them (Fig. 4).
Figure 4. DataTable class diagram
Data Managers comprise the last part of the library. Each such class incorporates by composition a model class and an appropriate backend class, yielding "backend-managed" models. Applications using this library should typically instantiate and keep a reference to one of these classes. The library defines three manager classes, including two individual and one composite.
DataRecordManager (Fig. 3) and DataTableManager (Fig. 4) implement IDataRecordManager and IDataTableManager interfaces and manage their respective model classes. Additionally, since DataRecordModel and DataTableModel may work cooperatively (one holding a record from the recordset held in the other), the data may need to be transferred between the two model classes. Thus, a third composite manager is necessary.
DataCompositeManager is used where DataRecordModel and DataTableModel work together, and it handles data transfers between the model classes. A composite manager can encapsulate either two backend-managed classes or two model and two backend classes directly. DataCompositeManager uses the latter approach (Fig. 5).
Figure 5. Composite manager
Lets us overlap the structural application schematic from the previous section and the class hierarchy, as shown in Fig. 6. Note that the figure has only one DataTable backend class, one Data Manager class, while abstract factory classes are not shown. The arrows indicate the flow of data, and the classes placed near the middle of these arrows facilitate/implement such transfers (ContactEditorPresenter is responsible for "GUI ⇔ DataRecordModel" transfer).
Figure 6. Functional class mapping.
The ContactEditorModel (MVP) and DataRecordModel/DataTableModel (Storage Library) classes act as a container for the data. As illustrated in the figure, the former encapsulates the latter via indirect composition through the DataCompositeManager class. The four classes thus bridge the MVP and Storage Library components.