Click here to Skip to main content
15,881,803 members
Articles / Programming Languages / C#
Article

Domain Objects Persistence Pattern for .NET

Rate me:
Please Sign up or sign in to vote.
2.17/5 (17 votes)
16 Jun 20054 min read 53.6K   30   5
Domain objects in an application represent the core data and business validation rules relating to it. Domain objects are usually central to the entire application and used by most subsystems. Therefore, their good design is critical for a good application design that is robust and high performing.

Abstract

Domain objects in an application represent the core data and business validation rules relating to it. And, domain objects are usually central to the entire application and used by most subsystems. Therefore, their good design is critical for a good application design that is robust, high performing, and yet flexible.

When it comes to developing object oriented applications that use relational databases, the domain object design has to be consistent with the database design. This make them easier to understand because they represent real-life "entities" and their relationships with each other. Therefore, in many situations, the domain objects are "mapped" to the relational database tables and relationships between tables. However, it is very easy to get this mapping wrong and end up with an undesirable domain object design. A good design for domain objects requires a solid understanding of object oriented and relational fundamentals on the part of developers.

Domain Objects Persistence Pattern attempts to provide a solution for domain object mapping to the relational databases that decouples the domain objects from the persistence logic. The domain objects in this pattern are unaware of the objects that persist them because the dependency is only one-way (from persistence objects to domain objects). This makes the domain objects design much simpler and easier to understand. It also hides the persistence objects from other subsystems in the application that are using the domain objects. This also works in distributed systems where only the domain objects are passed around. In this context, an attempt is made to incorporate the Factory Pattern into this pattern to help decouple domain objects and persistence logic.

Scope

Domain Objects, Persisting Domain Objects.

Problem Definition

Domain objects form the backbone of any application. They capture the core data model from the database and also the business rules that apply to this data. It is very typical for most subsystems of an application to rely on these common domain objects. This means that the closer the domain objects map to the data model in the database, the easier it is for the application developers to understand and use them because they mimic real-life "entities" and "relationships" as represented in the database.

If domain objects are not separated from the rest of the application, we end up with duplication of code everywhere. Similarly, if domain objects are not separated from the persistence code, we face situations where any subsystem using the domain objects also knows and depends on the persistence objects. And, any change in persistence objects affects the entire application, hence a bad design.

Solution

One way to achieve the above mentioned goals is to separate the domain objects into a separate subsystem and let the entire application use them wherever it needs domain data. Additionally, we should separate domain objects from the persistence code. This double-decoupling allows us on one hand to avoid code duplication and on the other to hide the persistence details from the domain objects and make it more flexible in case it needs to change. The domain objects and the rest of the application is totally unaffected whether the data is coming from a relational database or any other source (e.g. XML, flat files, or Active Directory/LDAP).

In separating the persistence logic from domain objects, we ensure that the domain objects have no dependency on the persistence code. This allows the domain objects to become available in environments where we don't even want to expose our persistence code.

Sample Code

In this sample, we will look at a Customer object from NorthWind database mapped to the "Customers" table in the database.

C#
public class Customer {
    // Private data members
    String      _customerId;
    String      _companyName;
    String      _contactName;
    String      _contactTitle;

    public Customer() {}

    // Properties for Customer object
    public String CustomerId {
        get { return _customerId; } set { _customerId = value;}
    }

    public String CompanyName {
        get { return _companyName; } set { _companyName = value;}
    }

    public String ContactName {
        get { return _contactName; } set { _contactName = value;}
    }

    public String ContactTitle {
        get { return _contactTitle; } set { _contactTitle = value;}
    }
}
public interface ICustomerFactory 
{
    // Standard transactional methods for single-row operations
    void Load(Customer cust);
    void Insert(Customer cust);
    void Update(Customer cust);
    void Delete(Customer cust);

    // Query method to return a collection
    ArrayList FindCustomersByState(String state);

}

public class CustomerFactory : ICustomerFactory
{
    // Standard transactional methods for single-row operations
    void Load(Customer cust) { /* Implement here */ }
    void Insert(Customer cust) { /* Implement here */ }
    void Update(Customer cust) { /* Implement here */ }
    void Delete(Customer cust) { /* Implement here */ }

    // Query method to return a collection
    ArrayList FindCustomersByState(String state) { /* Implement here */ }
}

Below is an example of how a client application will use this code.

C#
public class NorthwindApp
   {
       static void Main (string[] args) {
           Customer cust = new Customer();
           CustomerFactory custFactory = new CustomerFactory();

           // Let's load a customer from Northwind database.
           cust.CustomerId = "ALFKI";
           custFactory.load(cust);

           // Pass on the Customer object
           FooBar(cust);

           // custList is a collection of Customer objects
           ArrayList custList = custFactory.FindCustomersByState("CA");
        }
   }

As you can see above, the "load" method loads the Customer object from the database based on the CustomerId. Once the Customer is loaded, then it can be passed on to any subsystem in the application without exposing the persistence code. Similarly, if you get an ArrayList of Customer objects, you can pass on the ArrayList which has no persistence code dependency.

Conclusion

Using the Domain Objects Persistence pattern, we have extracted the persistence code out of the Customer object. This has made Customer object more object-oriented and simpler to understand because its object model is closer to the data model in the database. And, finally, we have enabled the Customer object to be passed around to different parts of the application (or even to distributed applications through .NET Remoting) without exposing its persistence code.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Marketing
United States United States
Iqbal M. Khan works for Alachisoft, a leading software company providing O/R Mapping and Clustered Object Caching solutions for .NET. You can reach him at iqbal@alachisoft.com or visit Alachisoft at www.alachisoft.com.

Comments and Discussions

 
GeneralWhen I see examples like this... Pin
Marc Clifton17-Jun-05 2:04
mvaMarc Clifton17-Jun-05 2:04 
I want to scream. There are better ways to abstract data persistence and to avoid coding CRUD statements into every class. Yes, sometimes you need to tell the computer exactly what needs to be done. 95% of the time, you can write a layer that, given the necessary information, can figure it out for you and perform the persistence. And then, instead of every class having a variant for the different persistent stores you want to support, you can add support at a unified layer of the application.

Marc

My website

Latest Articles:
Object Comparer
String Helpers
GeneralRe: When I see examples like this... Pin
WWalden17-Jun-05 7:12
WWalden17-Jun-05 7:12 
GeneralGood idea Pin
WillemM16-Jun-05 23:00
WillemM16-Jun-05 23:00 
GeneralRe: Good idea Pin
Thomas Lykke Petersen16-Jun-05 23:26
Thomas Lykke Petersen16-Jun-05 23:26 
GeneralRe: Good idea Pin
WillemM16-Jun-05 23:55
WillemM16-Jun-05 23:55 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.