Encapsulates data model and data access details within a relevant domain object. In other words, an Active Domain Object abstracts the semantics of the underlying data store (i.e., SQL Server) and the underlying data access technology (i.e., ADO.NET) and provides a simple programmatic interface for retrieving and operating on data.
Assume that data was obtained using a data accessor object. Applications that directly manipulate this data lend themselves to several problems:
The Active Domain Object (or the Data Access Logic Component) addresses these issues by encapsulating a data model and code to manipulate that data model in a set of domain objects. The application then uses the Active Domain Object to access and manipulate data.
The term active refers to domain objects that do more than simply represent data. They expose logical operations to take care of relevant database interactions. Some of these logical operations (which are named using domain terminology and not data access terminology) include:
An ActiveDomainObject defines logical attributes and operations that represent domain concepts. It implements its operations by interacting with a Data Accessor object to access the database. It also handles converting data between its database form and its application form.
An application instantiates an ActiveDomainObject, which initializes itself with data from the database. Once an ActiveDomainObject is initialized, the application can access its properties. The application can then ask the ActiveDomainObject to save its state to the database.
This example define an active domain object that represents a customer:
public class Customer
{
/* Data members */
private string
m_strConnection = ""; //
Used by the data accessor object to access database
private System.Data.DataRow drCustomer
= null;
private
Address
obAddress = null;
/* Constructors */
public Customer()
{
// Acquire the connection string from
a connection-string factory-object
m_strConnection = ConnectionStringFactory.GetConnectionString();
}
/* Data access methods */
public System.Data.DataRow
GetCustomer( int nID )
{
// Use data access helper components to retrieve
customer data via a stored procedure
drCustomer = ...
// Construct an Address object from
customer data
obAddress = ...
// Return customer information
return drCustomer;
}
public int CreateCustomer( string strName,
Address obAddress, string DateTime dtDOB )
{
// Use data access helper components to create a new customer entry in the database
// and return customer ID
}
public void DeleteCustomer( int nCustomerID )
{
// Use data access helper components to delete customer from the database
}
public void UpdateCustomer( DataRow drCustomer )
{
// Use data access helper components to update customer in the database
}
/* Properties */
public string
Name { get { return (string)drCustomer["Name"];
}
public DateTime MemberSince { get { return
(DateTime)drCustomer["RegistrationDate"]; }
public bool IsActive
{ get { return (bool)drCustomer["Active"]; } }
public
Address
Address { get { return obAddress; } }
}
public class Address
{
/* Data members */
private
string strAddress1 =
"";
private
string strAddress2 =
"";
private
string strCity
= "";
private
string strPostCode =
"";
private
string strCountry =
"";
/* Constructors */
public Address (string add1, string add2, string
city, string pc, string cntry )
{
// Populate
data members
}
/* Properties */
public string Address1 { get {return strAddress1; } }
public string Address1 { get {return strAddress2; } }
public string City { get {return
strCity; } }
public string PostCode { get {return strPostCode; } }
public string Country { get {return strCountry; } }
}
public class ConnectionStringFactory
{
public static string GetConnectionString()
{
// Retrieve
connection string from somewhere - config file, etc.
}
}
// This Application-code block illustrates how to
create and initialize a Customer object
Customer obCustomer = new Customer();
Address obAddress = new Address( ... );
obCustomer.CreateCustomer( "Yazan", obAddress, dtDON );
Note how Customer collates address data from drCustomer to form an Address object. Address can be considered as a domain object, but is not active in the sense that it is really part of Customer objects; it is Customer objects that handle the mapping of properties found in Address objects. This also allows the Address object to be reusable by other parts of the application. Also note that ConnectionStringFactory encapsulates connection string management for Customer and other active domain objects.
The following complete example uses CollectionBase to create a specialized collection that only accepts objects of type Customer instead of accepting and exposing contained objects as Object.
// CustomerCollection class that
only contain objects of type Employee
public class CustomerCollection : System.Collections.CollectionBase
{
/* Constructors */
public CustomerCollection() {}
/* IList interface implementation */
// The Add method declaration permits
only Customer objects to be added
public void Add( Customer obCust )
{
this.List.Add( obCust );
}
// Implement a Remove method
public void Remove( int nIndex )
{
// Check if
index is valid
if ((nIndex < 0) || (nIndex
> this.Count - 1))
throw new
ArgumentOutOfRangeException( "nIndex", "Index is not
valid");
this.List.RemoveAt( nIndex );
}
// Implement an indexer that returns
only Employee objects (and not object of type Object)
public Customer this[int nIndex]
{
get { return (Customer)this.List[nIndex];
}
set { List[nIndex] = value; }
}
}
private void CollectionBase_Click(object sender,
System.EventArgs e)
{
// Create some employee obejcts
Customer obCust1 = new Customer();
obCust1.CreateCustomer( ... );
Customer obCust2 = new Customer();
obCust2.CreateCustomer( ... );
// Create our specialized collection
CustomerCollection coll = new CustomerCollection();
coll.Add( obCust1 );
coll.Add( obCust2 );
}
Use this pattern when:
The main motivation for defining active domain objects is to make application code easier to write and maintain, therefore, it makes sense to tailor domain objects to application concepts.Consider these strategies when designing an active domain object.
If applications need only certain properties, then provide those only. Also
consider whether the physical form of the data is suitable for applications.
For example, a BLOB column can be exposed as a C# MemoryStream
property. It may also make sense to form a single property form multiple
columns.