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.
Domain Objects Persistence Pattern for .NET
Posted by: Iqbal M. Khan; AlachiSoft;
www.alachisoft.com
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.
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();
// 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.
Author: 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 [email protected]
or visit Alachisoft at