šŸ’½ Data Access Patterns

back

Our software architecture enjoys the benefits of ORM technology.
These patterns show how you might structure your code.

Contents

Entities

Entities are the classes that represent the functional domain model.

Contents

Pure Data Objects

In this architecture, we aim to keep the Entity classes just data and free of logic. The Entities in this architecture have properties of simple types and references or lists to other Entities.

class Supplier
{
    int ID { get; set; }
    string Name { get; set; }
    Address Address { get; set; }
    IList<Product> Products { get; set; }
}

Enums

You might even want to avoid enums in the Entity classes and put those in the business layer instead. Often the database contain enum-like Entities, which you could as Entities in your model. This to keep it a purer representaton of the data model:

class Supplier
{
    Industry Industry { get; set; }
}

class Industry
{
    int ID { get; set; }
    int Name { get; set; }
}

enum IndustryEnum
{
    Retail = 1,
    Travel = 2
}

Collections

Creating collections upon initialization is recommended for Entity classes. NHibernate does not always create the collections for us. By creating a collection we can omit some null checks in the code:

class Supplier
{
    var Products { get; set; } = new List<Product>();
}

Virtual Members

For Entity classes, public members should be virtual, otherwise persistence technologies may not work. This is because ORM's want to create Proxy classes, that tend to override all the properties.

class Supplier
{
    virtual int ID { get; set; }
    virtual int Name { get; set; }
    ...
}

Inheritance

Generally avoid inheritance within your Entity models, because it can make using data technologies harder.

Real Code

The previous code examples for Entities were just illustrative pseudo-code. This might be a more realistic example:

public class Supplier
{
    public virtual int ID { get; set; }
    public virtual string Name { get; set; }
    public virtual Address Address { get; set; }
    public virtual IList<Product> Products { get; set; } = new List<Product>();
}

Mapping

Mappings are classes programmed for a particular persistence technology, e.g. NHibernate, that map the Entity model to how the objects are stored in the data store (e.g. an SQL Server database). A Mapping defines which class maps to which table and which column maps to which property.

DTO

DTO = data transfer object. DTO's only contain data, no logic. They are used to transfer data between different parts of a system. In certain situations, where passing an Entity is not handy or efficient, a DTO might be a good alternative.

For instance: A specialized, optimized SQL query may return a result with a particular record structure. You could program a DTO that is a strongly typed version of these records. In many cases you want to query for Entity objects instead, but in some cases this is not fast / efficient enough and you might resort to a DTO.

DTO's can be used for other data transfers than SQL queries as well.

Repository

A Repository is like a set of queries. Repositories return or save Entities in the data store. Simple types, not Entities, are preferred for parameters. The Repository pattern is a way to put queries in a single place. The Repositories' job is also to provide an optimal set of queries.

Typically, every Entity type gets its own Repository.

It might be best to not expose types from the underlying persistence technology, so the Repository abstraction stays neutral.

Repository Interfaces

Any Repository type will get an associated Repository interface. This keeps our system loosely coupled from the underlying persistence technology.

The Repository interfaces are also handy for testing, to create a fake in-memory data store, instead of connecting to a real database. The API JJ.Framework.Data can help abstract this data access, providing a base for these Repositories and interfaces.

back