EBOOK The Data Access Layer: A PRAGMATIC APPROACH
01 06 Identifying the Optimal DAL Architecture What are Patterns 02 07 Decomposing the Data Layer Pass-Thru 03 08 Eager and Deffered Data Materialization Patterns Using Repositories 04 09 Concurrency Control Unit of Work Pattern 05 10 Full ORM and Micro ORM How to Select a DAL Pattern
here are several Data Access Layer (DAL) patterns used in software application development. Depending on the situation, some are a better fit than others. The DAL architecture best suited for your project depends on many factors. The types of databases, scalability concerns, data workload, frequency of schema changes, project timeline and number of developers on the project impact which DAL pattern is the best choice. In this ebook, we ll reference our development experience to detail how the DAL sits within an N-tier architecture. Then, we ll explain how the Business Logic Layer (BLL) communicates with the DAL, outlining several patterns developers may use when building certain types of applications. 01
Decomposing the Data Layer he DAL resides at the base of an N-tier architecture, enabling the BLL to process information needed to execute Presentation or Service Layer functions. These layers may utilize various data representation models to communicate. For example, while the Domain Model an object within the BLL represents the application s use case for data, the DAL materializes the Data Model as a representation of the data persisted in the database. When handling multiple data representations, the DAL must meet four responsibilities: 1. Persist data to the database Storing the data in a persistence medium. 2. Retrieve data from the database Calling data through the data access sub-layer. 3. Leverage object mappings to convert raw data into an object representation (data model). 4. Insulate the BLL from lower level details like persistence mechanism, structure and query construction. Let s dig a little deeper on that third obligation, which concerns object production. 02
Eager and Deferred Data Materialization ata materialization is the process of transforming raw data into data models the applications can utilize. There are two types of data materialization: Eager and Deferred. Eager materialization converts data into objects within the DAL. This creates a leaner BLL and enables better testability and troubleshooting regarding whether an issue pertains to logic or data. The main benefit of this approach is a strong boundary between the DAL and other layers which promotes good encapsulation. Deferred materialization allows for full object creation outside of the DAL through lazy loaded properties and collections that retain DAL references. While deferred materialization is powerful, it does introduce challenges: Various race conditions in multi-threaded applications. Performance issues related to excessive database calls. Unexpected exceptions due to disposed DAL references. Partially loaded classes that may cause confusion to the unexpecting developer. Inconsistent state between master and detail records due to time of read in volatile data situations. 03
Concurrency Control oncurrency control ensures consistency among interacting components within an application. This prevents data materialization and persistence functions from violating the integrity of data. The DAL (or application) dictates different levels of transactional isolation, the most common of which are: None: The last writer wins. Optimistic: First writer wins. Pessimistic: First to acquire exclusive lock wins. Application circumstances dictate the preferred transaction option. For example, if it s likely multiple users will try to access the same data simultaneously, then pessimistic transactional isolation is preferable. In contrast, optimistic concurrency control is advantageous when there s a low risk of two users making adjustments at the same time. 04
Full ORM and Micro ORM bject Relational Mappers (ORMs) are used to automate conversion of relational data into objects. Developers have two options: Full ORMs and Micro ORMs. Full ORMs provide rich feature sets and tooling that expedite project construction and definition of database models. Some advantages are: Built-in change tracking. Automatic SQL Generation. Custom data mappings. Micro ORMs have limited but focused features: Speed and performance. Light configuration requirements. Easy learning curve. The choice between Full and Micro ORMs come down to finding the balance between customizability and simplicity, between feature richness and performance. 05
What are Patterns? atterns dictate how the application and the DAL interact with each other by way of determining how the latter operates. More specifically, they dictate not where data will be converted into objects, but how such manipulation will occur. There are five types of patterns: Pass-Thru: A minimalist pattern to access data with no contract. Static Repositories: Defines the contract using static methods. Instance Repositories: Defines the contract using instance methods Unit of Work: Contains a sequence of actions to execute against objects and track changes. Depending on your project necessities, one of these patterns will emerge as a better fit than the others. 06
Pass-Thru he Pass-Thru method is the thinnest DAL you can have. Further minimalism would dictate that you would handle all the ORM aspects yourself. If you were using Entity Framework for example, you would have a project with your generated models, mappings and context only. Pass-Thru s lean composition gives developers the freedom to retrieve data in multiple ways. It is up to each developer using this DAL to compose his or her own method of retrieving data using the generated code. This pattern works well with deferred data materialization, since the BLL contains references to the database context directly. The Pass-Thru DAL method affords the developer much flexibility, but it also can lead to inconsistent and redundant code. In addition, it is difficult to create unit tests for this type of DAL, given there s no custom code exposing a clear interface. 07
Patterns Using Repositories he repository pattern provides a layer of abstraction between the database and the BLL. It exposes a contract that can be used for unit testing. The two repository-based patterns are Static and Instance Repositories. The Static Repository exposes static methods, eliminating the need for callers to create instances or configure the repository before accessing it. This minimizes BLL-level work, and enables every repository to use other repositories, which can support a rich developer experience. Static repositories need to be aware of thread safety. As the name implies, an Instance Repository necessitates instantiation. This pattern often uses an interface through which the caller constructs an instance, implements it in the local scope and then discards it. A repository may have an empty constructor, or one which possesses dependencies on other repositories. As instantiation becomes more of a burden, the developers will have to manage coupling. This could be addressed using IoC containers, but that introduces additional complexity. 08
Unit of Work Pattern he Unit of Work (UOW) pattern tracks a sequence of actions to perform on a database. The primary benefit to the UOW pattern is that the BLL can focus on business logic rather than coordinating database details. The logic is free to make any changes needed and can be loosely composed. After all the business logic has been completed, the application can commit the changes to the database. Data changes are performed as a single transaction so that either the entire set of changes succeeds or no changes are made. The transaction is used to maintain database integrity. 09
How to Select a DAL Pattern he pattern you select will largely depend on the project timeframe, application complexity and scalability. Testability and maintainability become important aspects to consider in products or long-term projects. Listed below are the project parameters and the patterns that would best complement them: Low complexity: Pass-Thru Medium complexity: Static Repository Highly testable: Instance Repository Transactional: Unit of Work High encapsulated: Eager materialization Highly dynamic: Deferred materialization Bear in mind that the patterns listed are not exclusive of one another. Features and methods associated with each may be combined to support your application s function. 10
11