Active Domain Object

Summary

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.

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:

Initialize: Initializes the domain object by reading data from one
or more tables.

Refresh: Refresh the domain object's contents from the database to
ensure consistency.

Save: Save changes to the domain object's contents by inserting or
updating.

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.

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.

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.

Domain objects and tables
Domain objects do not necessarily correspond directly to a single table. How
you align domain objects with tables depends on the granularity that
applications require. For example, a Customer
domain object may correspond directly to the [Customer]
table, whereas an Order object may correspond to
a join between [Order] and [OrderDetails]
tables.

Domain objects and properties and columns
It was mentioned in the pre-requisites
section that an active domain object corresponds to a data access logic
component which encapsulates data model and data access details within a relevant domain
object. The active domain object needs to expose its data using some or all
of the following properties:

Properties to represent the entire data
This often corresponds to a single property that returns an ADO.NET DataSet
or DataTable.

Properties to represent a specific row
This often corresponds to a single property that returns an ADO.NET DataRow.

Properties to represent individual columns within a single table row.
Domain object properties (set and get
properties in C#) do not necessarily correspond directly to those column
obtained from the first two types of properties.

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.

Domain logic
Domain objects can define behavior. In fact, exposing common logical
operations from a domain object is often less error-prone than exposing
properties and expecting application code to implement the same logic. You
may also find that you do not expose certain properties at all if domain
object operations are sufficient for the application. For example, a Customer
domain object maps to the [Customer] and [Address]
tables. Rather than expose properties to retrieve each column of the
customer's address, you can define a GetAddress
operation to concatenate the address columns in a standard address format.

Define a consistent connection management strategy
Define a connection-string factory object that is globally accessible and hands off
a connection string to any domain object that requires one. This provide a
consistent way of handing a connection string to all domain objects.

Spreads data access across multiple domain objects
Each domain object is responsible for its own data access implementation.
Spreading this responsibility across multiple domain objects means that
changes to data access strategies affect every component. For example, each
domain object like Customer and Product may initialize a data accessor by
passing the data accessor's constructor a connection string. You may decide
to change this and instead pass the connection string directly to each data
accessor method. This change is likely to affect every domain object.

Limits application control of data access
Application code that interacts with domain object is limited to the
properties and methods exposed by these domain objects. If these
domain objects are not well designed, then application code may need to
resort to un-natural or awkward workarounds.