Author: Craig Stancl, Kevin Peterson
Email: Stancl.craig@mayo.edu, peterson.kevin@mayo.edu
Team: LexEVS
Contract: CBITT BOA Subcontract# 29XS223
Client: NCI CBIIT
National Institutes of Heath
US Department of Health and Human Services

Revision History

Version

Date

Description of Changes 

Author

1.0

5/14/10

Initial Version Approved via Design Review

Team

Data Access Layer

Solution Architecture

LexEVS lacks a generalized Read/Write interface that can support Authoring and incremental Coding Scheme changes. Also, database access is not isolated. Database-specific code is intermixed with the Service Layer, as well as Extensions. To make LexEVS more able to handle Authoring, while at the same time providing a clean, centralized Database Access Layer, these are the requirements:

Structure

Main Components:

Sub Components:

diagram of components

Domain Driven Design

The LexEVS Data Access Layer loosely follows the Domain Driven Design (DDD) Principals.

DDD Principals and the LexEVS Data Access Layer equivalent:

Service-Based CRUD and Component Interaction

General Component Flow:

External Calling Service

The Component, API, or Application that requires access to the database. The reference to the Service Manger is obtained through a Service Locator pattern, or by other means outside the scope of the Data Access Layer. The External calling service does not (and should not) use any JDBC specific code, including Transactional Demarcation.

;Service Manager

The Service Manager is the main entry point to all of the Data Access Services. The Service Manager provides a centralized reference for all of the available Database Access Services, allowing clients to select the Service or Services that they require.

Individual Service

Database Access is broken into groups of 'Services'. Each Service will be a logical grouping of functions loosely based on packages of the Object Model. A service serves these rols:

;DAO Manager

The DAO Manager serves the same purpose as the Service Manager -- to provide a central lookup point for all available DAOs in the system.

The DAO Manager is implementation independent, so it must follow certain rules:

Individual DAOs

Each individual DAO will be responsible for a logical set of CRUD database operations. DAOs themselves are expected to be implementation specific, MAY be LexGrid Schema Version specific and MAY be Database Type specific. So, for example, there may be one DAO for Oracle, one for DB2, and so on. The DAO is expected to provide enough information about itself for factories to be able to select the appropriate DAO for the DAO Manager.

DAOs may be implemented in any framework, as long as the adhere to the Interface contract of the DAO.

DAOs may NOT define Transaction boundaries within themselvs.

DAOs MUST fire the appropriate events as content is updated, inserted, or deleted.

Event Framework

The Data Access layer will support an Event-Based framework to allow External Calling Services an access point to monitor the changing state of the Database The Data Access Layer Event Framework will follow these guidelines:

Insert Event

Parameter

Output

Item to be inserted

A boolean indicating whether or not the insert should take place

Delete Event

Parameter

Output

Item to be deleted

A boolean indicating whether or not the delete should take place

Update Event

Parameter

Output

Item to be updated (before changes)


Item to be updated (after changes)

A boolean indicating whether or not the update should take place

Implementation - Database

Ibatis

Apache Ibatis is a framework for decoupling SQL code from Java code. All SQL code is defined in XML files as opposed to in the Java classes themselves, making updates and edits to the SQL. Also, SQL code can be modified without recompiling the system.

Reasons for Ibatis:

An example Ibatis XML document is shown below. Note that the SQL code, as well as the logic for assembling the results, is decoupled from the Java implementation:

Ibatis vs. Hibernate

Hibernate is an Object Relational Mapping tool, Ibatis is not. Hibernate aims to abstract the developer from building SQL code, as all SQL is generated by the framework. Ibatis does not build or generate SQL code, but gives the user direct control of the SQL.

Hibernate assumes a certain database structure, as well as a specific Object Model structure to be able to function properly. There are several problems with using Hibernate with LexEVS.

Ibatis vs. Spring JDBC

Spring JDBC is a JDBC framework that abstracts the developer from boilerplate JDBC code, such as managing connections, error handling, mapping rows to Objects, etc. It uses Template Patterns to provide commonly used JDBC functionality. Spring JDBC is powerful, flexible, and fast -- but it does not decouple the SQL code from the Java Code. Ibatis accomplishes much of the same thing as Spring JDBC -- they both allow dynamic SQL building and direct control of the SQL code.

Caching

Caching commonly accessed data is critical for performance, and the DAO Layer will have a built-in caching framework to accommodate this. The framework is annotation-based and could be generalized to other use-cases, not just the DAO layer.

Annotations

@Cacheable

The @Cacheable annotation marks a class as being eligible for method caching. Classes without this annotation will not considered by the Caching framework

This annotation accepts 2 parameters:

Parameter Name

Description

Required

cacheName

The unique name of the cache. This cache may be shared between classes, or each class may have its own unique cache.

YES

 cacheSize

The number of cached elements to be held in memory. Once the limit is reached, the element that has be accessed
 will be removed

NO
(Default = 50)

@CacheMethod

The @CacheMethod annotation is placed on an individual method to be cached. Method caching follows the following guidelines:

@CacheMethod
public CodingScheme getCodingSchemeById(String codingSchemeId)

In the example above, the String 'codingSchemeId' is evaluated as the cache 'key'. If future calls to the method have the some 'codingSchemeId' parameter - the method will not execute, and the result will be resolved from the cache.

NOTE: The cache key is determined by the 'toString()' representation of every parameter passed to the method.

@ClearCache

If a call to a method in a Cacheable class will invalidate the cache, the @ClearCache annotation may be used to reset the cache. An example use-case if this would be an 'update' method. The 'update' will change the underlying data in the data store, but the cache will still contain the old data. A @ClearCache annotation may be added to any 'update' method to reset the cache whenever the underlying data may have changed.