[ Team LiB ] Previous Section Next Section

Strategies

As you define domain objects to use in applications, it is important to decide whether to model application concepts or data model concepts more closely. If you are creating an application and its data model from scratch, then you can align these concepts so they match. However, as applications mature, these two concept sets do not always change consistently. The whole motivation for defining domain objects is to make application code easier to write and maintain, so it makes sense to tailor domain objects toward application concepts. Here are some ideas to consider as you make this distinction:

  • Domain objects and tables— You do not necessarily need to define domain objects that correspond directly to rows in a single table. For instance, you can define a domain object as a collection that represents query results. A domain object can correspond to a join rather than a table. You can define one domain object to correspond to only a subset of a table's columns and another domain object to correspond to the remaining columns. How you align domain objects with tables depends on the granularity that you expect applications to require.

  • Attributes and columns— You do not necessarily need to define domain object attributes that correspond directly to table columns. If you do not expect applications to reference certain columns, then there is no reason to incorporate them in a domain object. Likewise, if applications only need to read specific data, then a domain object only needs to provide get operations for the corresponding attributes.

    Also consider whether the physical form of the data is convenient for the application. If it is not, then a domain object can convert transparently between its physical and logical forms. It may even make sense to form a single attribute from multiple columns or vice versa.

  • Separate collections from objects— It is common to define domain objects that represent a single table row. Applications often need to manipulate several rows at a time to process query results or issue batch updates. It usually makes sense to consider collection domain objects differently from single-row domain objects.

    A collection domain object encapsulates query and batch update operations and usually hands back related single-row domain objects for the application to manipulate. You can package a collection domain object and single-row domain object together to form a single component that provides all domain operations for one or more related tables. These two classes can also share common aspects of their domain object mapping code.

  • Domain logic— Domain objects are object-oriented and can define behavior. It follows that you can attach related behavior and processes as additional operations on a domain object. You may find that exposing common logical operations is less error-prone than exposing attributes and expecting application code to implement the same logic. You may also find that you do not need to expose some attributes at all if applications can sufficiently manipulate them through the domain object's operations.

  • No usage penalties— It is essential that you define domain object attributes and operations to be both convenient and useful. Ensure that applications do not need to employ unnatural semantics or glue code to use your domain objects. If application developers employ odd sequences of code repeatedly, you may need to enhance or rethink your design. Effective use case analysis helps to avoid this problem.

  • No performance penalties— Domain objects hide many details from callers and this fact makes application code easier to write and maintain. However, the trade-off is that callers have much less control over some aspects of data access such as resource management and physical database operations. Ensure that common usage patterns result in optimal data access operations.

Here are some other ideas to consider when implementing the Active Domain Object pattern:

  • Define a consistent connection management strategy— Connection management addresses how active domain objects resolve and share the physical database connections they use for reading and writing the data they represent. You may find it helpful to use a consistent connection management strategy for all active domain objects in a system.

    One possibility is for active domain objects to manage their own connections. You might create and store a connection for each domain object, or share one among all domain objects of a particular type. This choice is clean and hides connections within domain object implementations, but it may not be the most efficient way to manage connections since the number of connections can be large and they tend to be left open long after the application code requires their services. These characteristics can have a significant effect on an application's scalability and performance.

    A second possibility is to have calling code pass a connection to active domain objects whenever data access is required. This takes the form of an extra parameter to relevant domain object operations and constructors and makes the class's public operation signatures more complex. In addition, it sometimes requires your code to pass connections several levels deep through a domain object's call stack. This strategy imposes the burden of connection management on the caller, but it does allow the caller to efficiently manage a small set of connections that all domain objects share.

    A final approach is to define a connection factory object that is globally accessible and hands off a connection to any code that requests one. This enables active domain objects to request connections as needed, without requiring callers to pass them explicitly on operation or constructor calls. A connection factory encapsulates the connection management strategy within a single component and makes it easier to add new connection types or optimize and use a connection pool.

  • Maintain a saved state— You may find it necessary to maintain a saved state for each active domain object. In most cases, this is simply a Boolean value that indicates whether a particular object's data matches the database. A domain object that an application creates to represent new data sets this flag to false. A domain object that loads its contents from the database sets this flag to true.

    An active domain object can use this state information when an application calls its save operation. If its saved state is true, it deduces that the data already exists in the database and it issues an update operation. If the saved state is false, the domain object knows that it is saving the data for the first time and it issues an insert operation.

    An active domain object can also use state information to keep track of whether its attributes have changed since it was last loaded or saved. In both of these cases, the saved state is only referenced within the active domain object's code, so you can define it to be a private attribute.

    [ Team LiB ] Previous Section Next Section