Methods to shift your enterprise.TM  

Domain Dependency Resolver

Define a strategy for resolving the dependencies that a use case or user story has on the domain model by ensuring that any lazy-loaded entity objects of a given aggregate required by the client are read from persistent storage while the required storage mechanisms are still available.

domaindependencyresolversummary

Background

When using a Domain Model the object graph that falls out from domain-driven design is often complexly interconnected. The domain model objects are generally stored in a relational or object database for persistence. In order to reconstitute an aggregate by reading it from the database into memory the aggregate must be built with all its aggregated entities. However, in some cases reading aggregates into object form could cause a large portion of the persistent store to be brought into memory all at one time. Such behavior is very undesirable as it is both slow and burdensome to system memory usage, possibly even preventing the application from working at all.

To prevent this from happening we may use lazy loading. When a root entity object is loaded its aggregated entities are only loaded when they are needed. Modern object-relation mapping (ORM) libraries commonly create a proxy object and set it as the property reference in place of the actual entity. When the proxy object is accessed for use it first loads the real aggregated entity object from the database, sets it into its containing parent's property reference, and then provides that object reference to the consumer who requested it.

While lazy loading works well when all necessary persistence mechanisms (database connection, ORM session with first-level cache, and the Unit of Work or transaction) are still in play, a “gotcha” is that it completely fails after those necessary mechanisms have been released. In N-tier architectures the persistence mechanisms are generally available when a service in the application layer is in context. After the application service method has completed and answered a results to its consumer, the persistence mechanisms are no longer available.

In a web-based application a controller object (of the Model-View-Controller or MVC kind) will generally use a service to get a payload of Domain Model data through a coarse-grained method. Once the data has been received from the service all persistence mechanisms are closed. So if the controller or a Dynamic Web Page tries to access lazy loaded data, the access attempt will fail because the proxy will try to use the now unavailable persistence mechanism.

The problem can of course be solved by not providing domain objects for direct use by the client tier. Instead the application layer's services would reply with a fine-grained Data Transfer Object (or View Bean if you prefer). As a fine-grained Data Transfer Object is being built, all lazy loaded data is automatically resolved in the service while necessary persistence mechanisms are still available. The problem with this approach, however, is that you must create a plethora of behaviorless classes that are used only to house data properties to layers above the application layer. In doing so we tend to replicate the entire structure of our Domain Model in a Data Transfer Object class hierarchy. In some cases it's even worse, we create a Data Transfer Object class hierarchy that more closely adheres to the use cases or user stories that we are servicing! Not only is this burdensome to develop and maintain, it influences services and related data to be less reusable by design, and it also requires many more object instances to be created to handle a single request. That spells development and runtime overhead. Of course this is not to mention the EJB-smell we will give to our otherwise awesome architecture. ;-)

Note: There is another lazy loading resolver pattern called Open Session In View. This pattern stipulates that the persistence mechanisms are managed at the MVC view level. That means that lazy loaded domain aggregates are resolved when they are referenced by the various views that use them. However, I consider the Open Session In View pattern to be harmful. There are several reasons for my strong opinion against its use. Not the least of my disdain for it are database access, including transaction control, is managed by the view, and the complexity of error handling when a database failure occurs. Add to this the inherent inability to test the many failure paths that could occur when the view accesses the database, and you have the potential for very unsatisfactory application characteristics on your hands. Although I have not here identified every reason that I strongly discourage its use, I think I have provided enough evidence that most will also consider harmful the Open Session In View pattern. It seems to me that its use may be justified in cases where the application is very simple. Interestingly, however, with such an application it is possible that the domain model is simple enough that lazy loading would not be necessary.

Many agree that a better approach is the client tier's direct use of domain objects—sometimes called detached objects because once the persistence mechanisms are no longer available domain objects are no longer attached to first-level cache in an ORM session. Although we will still use a kind of Data Transfer Object—the Domain Payload Object pattern—to return data to application service consumers, those objects are just simple coarse-grained domain object containers. Even more, by using domain objects directly in the client tier we move much closer to the original definition and intent of the Model-View-Controller pattern.

Note: The Domain Dependency Resolver pattern may be best used in an application where the client and application service layers are on the same physical tier—the same process space. Since some lazy loaded aggregated entities may not be resolved for a given use case or user story, a proxy object that manages lazy loading will be left in place of unresolved entities. If the domain objects are serialized to a remote client it may be either impossible or impractical to serialize the proxies. Further, if you attempt to work around this by replacing proxies with null values in their container's references, you could cause problems with your data. If your database session is flushed from first-level cache back to the database you could end up either nulling relevant data at worst or causing not-null constraint violations at best. If you do replace proxies with nulls you will no doubt need to detach the altered entity instances from the persistence session before committing the transaction, or you will need to ensure that the transaction is read-only. If you don't replace proxies with nulls you would need to ensure that proxy classes are available on the client side for the purpose of de-serializing; that is, if the proxies can be serialized in the first place. Hence, if you are using Remote Facade on your application service layer you will need to determine the possibility and practicality of this pattern.

But the problem remains. How do we resolve lazy loaded aggregates in preparation for client tier access? We need to create a set of strategies to help the application service layer or domain layer to resolve the lazy loading dependencies for us. The traditional Strategy pattern allows us to define a family of algorithms, encapsulate each one, and make them interchangable. Each strategy is matched to the consumer that needs it. To be clearer, our Domain Dependency Resolver focuses on strategies for resolving Domain Model lazy loading dependencies.

Value and Benefits

Allowing your entire application to adhere close to and benefit from the careful design of your Domain Model has considerable worth. It is, after all, the heart of your software. This is not to say that you reduce your Domain Model to exposing all entities in tact. You should use whatever Domain Model design strategies that make sense (such as answering a Value Object in place of an Entity, where appropriate). The point is, that your client tier uses the Domain Model directly in the same way that consumers closest to it are permitted to use it.

Reducing the number and complexity of class hierarchies such as Data Transfer Object classes makes your application easier to develop, easier to refactor, easier to test, and easier to maintain over its full lifecycle.

Separating the dependency resolution strategies from the clients, application services, and domain model business logic allows any number of necessary strategies to be developed and employed. Since different use cases or user stories have different dependencies on the domain, using strategies allows each resolver to be tuned to the use case to or user story that it works in behalf of. That way no one feature has to reuse domain object resolution behavior that provide too much safely referencable data. You get just what you need; nothing more, nothing less.

Putting It to Work

The basic concept of the GoF Strategy pattern does not change here. We need a common interface to represent a Domain Dependency Resolver. The interface must support at least one method for resolving client dependencies contained by a given Entity:

basicinterface

The resolve() method takes a single parameter, which is the entity object that contains aggregated lazy loaded entities that a given client is dependent on. When the resolve() method is invoked the specific strategy of the implementing concrete class is employed to access needed aggregated entities, resolving lazy loaded references.

I like to use the Execution Context to register the name/identifier of or object instance of the Domain Dependency Resolver to be used. The client consumer places the identifier or instance into the execution context and invokes a application service method. The service method answers the Domain Payload Object with properly lazy load resolved domain objects.

You will need to determine if the resolvers should be an application service responsibility or a domain responsibility. If your domain is highly compartmentalized and requires the use of code that invokes private/protected property accessor methods to force lazy loads, it may be best to implement and execute the resolvers in to domain layer. If your domain model can be navigated sufficiently by a higher layer to force lazy loads, your resolvers can be implemented in the layer that is best at specifying the strategies and executed in the application's service layer. Technically speaking the actual resolver strategies themselves are more closely tied to the use cases or user stories and thus should be implemented (or specified) by the client layer. In a web application the MVC controller would provide the required resolver (identifier or instance) to the coarse-grained service method it uses, and the service method would access the domain to get the requested objects and then execute the resolver against the entity/entities. I like to use the Execution Context to reduce noise on the application's service API, relieving it from the burden of providing method parameters for this crosscutting concern.

This pattern has a few different strategies for implementation of the interface's resolve() method. The pattern strategies are not to be confused with different lazy loading strategies needed by each use case or user story. Think of Domain Dependency Resolver pattern strategies as the optional approaches that could be used to implement the resolve() method, not as the actual detailed data resolving code required by each different client tier use case or user story.

Hard-Coded Aggregate Access Strategy

This is the easiest pattern strategy to implement for any given Domain Dependency Resolver concrete class. Your resolve() method for a given concrete class just hard codes references to the root entity's aggregated child entities using simple accessor navigation. Using Eric Evans' Cargo domain example (DDD say page 456), we might have a use case or user story that requires a view of all TransportLeg instances:

cargoresolver

Assume that a Cargo's Itinerary and each of the Itinerary's TransportLeg instances are lazy loaded. We would need to implement the resolve() method as follows:

public class CargoItineraryTransportLegsResolver
    implements DomainDependencyResolver {
    
    // . . .
    
    public void resolve(Object anEntity) {
        
        Cargo tempCargo = (Cargo) anEntity;
        
        Itinerary tempItinerary = tempCargo.getItinerary();
        
        for (TransportLeg tempTransportLeg : tempItinerary.getTransportLegs()) {
            
            tempTransportLeg.load();
        }
    }
}

This hard-coded access strategy works fine. It's benefit is that it is simple to understand. The downside is that the resolve() method has a tight coupling with the Cargo root entity and its aggregated entities. That's not a big problem in itself. After all, the Domain Dependency Resolver is about domain objects and their contents. Nonetheless, if you have to implement tens or hundreds of such resolvers you are going to notice a lot of application's resolver strategies overlap. Of course you could create a class hierarchy of such resolvers attempting reuse, but that may tend to get unwieldy. There is a cleaner way using the second of two pattern strategies I provide.

Navigation Specification Strategy

This strategy decouples the entity object navigations from the resolver. Doing so allows there to be just one resolver (or two) in the entire application. The resolver simply interprets the navigation specification, and walks the root entity object and its aggregated entities forcing needed lazy loading.

A specification can be as simple as a collection of text strings of entity property names. If a text string has just a single property name, it represents a property name of only one property on the root entity (Cargo in our example). If a text string has a dot separated list of property names, it represents a navigation from the root entity down to the furthest required aggregated entities:

        itinerary
        itinerary.transportLegs

This first text string informs the resolver to navigate just to the immediate child entity named itinerary. The second text string informs the resolver to navigate first to itinerary and them from itinerary to transportLegs. If there are very deep aggregate associations you can specify dot-separated navigations as deeply as necessary.

Note however in the above example that the transportLegs reference is a collection. Each element in transportLegs needs to be lazy loaded. Depending on the ORM library you are using (or your own home-grown persistence lazy loading mechanism) you may need to do more than simply access the collection's reference in order to load all its elements. It is possible that you will have to directly access one or all of the elements to force their load (again depending on the tool you are using). In this case I like to add another qualifier to the specification:

        itinerary.transportLegs*

The asterisk/star/splat informs the resolver that it needs to iterate through at least one of the elements of the collection. The resolver can intelligently interpret the meaning and use the most optimal approach (one or all iterations, whatever works).

Note that in some cases, such as when using the Hibernate ORM, it may not be enough to access the lazy loaded aggregated entity itself. The resolver may have to access at least one simple property (primitive) or value object property within the object in order to force the proxy to lazy load the aggregated child entity it represents:

        itinerary.transportLegs*.preferred

This specification indicates that the resolver must iterate across the transportLegs, and as it does so access the preferred property on each TransportLeg instance.

Above I indicated that there may be as few as one or as many as two resolver concrete classes in the entire application. In some cases the domain layer's Repository data collectors may answer both single objects and full collections of objects. You may want to have a resolver that deals with collections of root domain entities and one that deals with single root domain entities. In that case you might create two concrete resolvers. On the other hand you could have a single resolver that has two resolve() methods, one that takes a single entity parameter and one that takes a collection parameter. Or your single resolver() method could interrogate its parameter and determine if it is a collection object was passed or a single object. I prefer the single resolver concrete class that has two overloaded resolve() methods:

interfacewithtwomethods

The resolve() method implementation uses a simple String parser to separate specification tokens. It then navigates from the root entity across aggregate using object introspection and reflection. Java, C#, Ruby, and other languages support this important feature:

public class DependencyResolver
    implements DomainDependencyResolver {
    
    private List navigationSpecs;
    
    public DependencyResolver(List aNavigationSpecs) {
        this.navigationSpecs = aNavigationSpecs;
    }
    
    public void resolve(Object anEntity) {
        for (String tempNavigationSpec : this.navigationSpecs) {
            
            String[] tempPropertyNames = tempNavigationSpec.split("\\.");
            
            for (String tempPropertyName : tempPropertyNames) {
                // . . .
            }
        }
    }
    
    public void resolve(Collection anEntityCollection) {
        for (Object tempEntity :  anEntityCollection) {
            this.resolve(tempEntity);
        }
    }
    
    // . . .
}

In the above Java example each DependencyResolver is instantiated with aNavigationSpec. The specification is a simple collection of text strings. Each text string contains one property name or a dot separated list of property names to navigate across. The nested loop in the first resolve() method shows access of each specification, and then iterates across the dot-separated properties. I do not show the object introspection and reflection. The remaining code is a simple exercise to work out.

I will comment, however, that your solution should consider the triggering condition that causes the ORM proxy or other lazy loading mechanism to execute. If the lazy load mechanism reacts on any property access then you may use direct property access introspection and reflection. The one advantage to using direct property access is that you can reach properties with private scope, allowing you to navigate through the domain model even when methods are not accessible outside the domain object class's package. On the other hand, if your domain model cannot be navigated using properties alone, you will have to use method-based introspection and reflection. It may be easier, safest, and more consistent to use accessor methods.

Consequences

Here are some tradeoffs and competing forces to consider.

  • Hard-Coding Is Simple: Hard coding resolution strategies are simple to implement, but you will spend more time creating and maintaining all the necessary classes. In the end you may have as many different strategies as you'd have Data Transfer Object Mappers. While I assert that using the Domain Model in the client tier is better than using fine-grained Data Transfer Objects, I still prefer where possible reducing the total number of classes an application requires.

  • Navigation Strategies Are Better: Maintaining a set of navigation strategy text string specifications is pretty simple. You can keep the text in configuration files and even have an IoC container inject them into your concrete Domain Dependency Resolver instances as needed.

  • House Resolvers In the Proper Layer: If your client (web) layer is the dependent, define strategies (or navigation specs) in the client layer. Communicate the dependencies to the application's service layer using Execution Context.

  • Select the Applicable Transfer Object: Data Transfer Objects were originally conceived for use with EJB Entity Beans, because you can't serialize an Entity Bean to a client tier. You can't really use a true Domain Model when Entity Beans are employed; or at least it is very, very difficult. If you are using a true Domain Model and you are able to return whole domain objects to your client tier, why give your application EJB-smell when it is unnecessary and more complex to do so? If you make an effort to embrace Domain Model directly in client tiers use Domain Payload Object.

  • Use Caution With Remote Facade: See the note above regarding use of this pattern and Domain Payload Object if you use Remote Facade. Serializing domain object entities that have lingering lazy loading proxies—because a specific client didn't require them to load real entity instances from persistence store—may be either impossible or impractical. You will have to either come up with a way of eliminating the proxy objects or find a way to serialize them, which may not be possible.

Related Patterns

The following patterns work well together with Domain Dependency Resolver.

  • Execution Context: Placing the name/identifier or object instance of the Domain Dependency Resolver to be used into an execution context frees your application service API from requiring an extra parameter on service methods.

  • Domain Payload Object: This coarse-grained object is tuned to the direct use of domain objects in the client tier. It has the same basic principles of Data Transfer Object, but is easier to create, map to, and maintain.