Using the Domain Objects Persistence Pattern in .NET

Using the Domain Objects Persistence Pattern in .NET

hen developing object-oriented applications that use relational databases, creating a domain object design that’s consistent with the database design makes applications easier to understand, because domain objects typically 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 developers to have a solid understanding of object oriented and relational fundamentals.

The Domain Objects Persistence pattern attempts to provide a mapping to relational databases that decouples the domain objects from the persistence logic. In this pattern, the domain objects themselves are unaware of the persistence mechanism, because the dependency is only one-way (from persistence objects to domain objects). This simplifies the domain objects’ design and makes them easier to understand. It also hides the persistence objects from other subsystems in the application that are using the domain objects. Even better, the pattern also works in distributed systems where only the domain objects are passed around, thus insulating the application from exposing the persistance mechanism to outside code. This article shows you how to incorporate the Factory pattern into the Domain Objects Persistence pattern to help decouple domain objects and persistence logic.

Defining the Problem
Domain objects form the backbone of any application. They capture the core data model from the database and the business rules that apply to that data. Typically, most subsystems of an application rely on these common domain objects?meaning that the more closely the domain objects map to the database schema, the easier it is for application developers to understand and use them because they mimic the real-life “entities” and “relationships” represented in the database.

When domain objects are not separated from the rest of the application, you often end up with duplicated persistence code in multiple locations. Similarly, when domain objects are not separated from the persistence code, you’ll face situations where any subsystem using the domain objects must also know and depend on the persistence objects. Any change in persistence objects necessarily affects the entire application; hence, failing to separate domain objects from the application and the persistence code is a bad design.

Defining a Solution
One way to achieve the above-mentioned goals is to separate the domain objects into a separate subsystem and let the rest of the application use them whenever it needs domain data. Additionally, you should separate domain objects from the persistence code. On one hand, this double-decoupling avoids code duplication, and on the other it hides the persistence details from the domain objects, creating a flexible design that’s easier to change. The domain objects and the rest of the application are totally unaffected whether the data comes from a relational database, an XML file, a flat file, Active Directory/LDAP, or any other source.

In separating persistence logic from domain objects, you must ensure that the domain objects have no dependency on the persistence code. Doing that allows you to expose the domain objects even in environments where you don’t want to expose the persistence code.Building an Example
This C# example uses a Customer object from the Northwind sample database mapped to the Customers table in the database.

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.

public class NorthwindApp{   static void Main (string[] args) {      Customer cust = new Customer();      CustomerFactory custFactory =          new CustomerFactory();      // 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");   }}

In the preceding code, the load method loads the Customer object from the database based on the CustomerID, value after which the application can pass it on to any subsystem in the application without exposing the persistence code. Similarly, if you load an ArrayList of Customer objects, you can subsequently pass on the ArrayList, which has no persistence code dependency.

Using the Domain Objects Persistence pattern separates the persistence code from the Customer object, making the Customer object more object-oriented and simpler to understand, because its object model is closer to the data model in the database. In addition, the separation lets you pass Customer objects to different parts of the application (or even to distributed applications through .NET Remoting) without exposing the persistence code.


About Our Editorial Process

At DevX, we’re dedicated to tech entrepreneurship. Our team closely follows industry shifts, new products, AI breakthroughs, technology trends, and funding announcements. Articles undergo thorough editing to ensure accuracy and clarity, reflecting DevX’s style and supporting entrepreneurs in the tech sphere.

See our full editorial policy.

About Our Journalist