BT
x Your opinion matters! Please fill in the InfoQ Survey about your reading habits!

Aspects of Domain Model Management

Posted by Mats Helander on Dec 23, 2007 |

Introduction

As can be learned from books such as Domain Driven Design [Evans DDD] and Applying Domain Driven Design and Patterns [Nilsson ADDDP], introducing the Domain Model pattern (Patterns of Enterprise Application Architecture [Fowler PoEAA]) into your application architecture may promise many benefits, but they do not come for free.

Using a domain model is rarely as easy as just creating the actual domain model classes and then using them. Soon enough one discovers that sizable amounts of infrastructure code will also be required in support of the domain model.

Prominent among the infrastructure demands that accompany a domain model is of course persistence - normally to a relational database. That's where Object/Relational (O/R) Mappers enter the picture. However, there's more to it than persistence. A large part of the infrastructure in a complex application will often be dedicated to managing the domain model objects during runtime. I refer to this part of the infrastructure as Domain Model Management [Helander DMM] or DMM for short.

Where Do We Put Our Infrastructure Code?

As the amount of infrastructure code grows, finding a good architecture for dealing with it all becomes increasingly important. A big part of the question is - are we allowed to put some infrastructure code in our domain model classes or is that something which should be avoided at all cost?

The argument for avoiding infrastructure code in the domain model classes is strong: The domain model is supposed to represent the core business concepts that our application is dealing with. Keeping these classes clean, lightweight and maintainable is an excellent architectural goal for an application that intends to make heavy use of its domain model.

On the other hand, as we will go on to see, taking the extremist route of keeping our domain model classes completely free of infrastructure code - often referred to as using a Plain Old Java/CLR Objects (POJO/POCO) domain model - can also prove problematic. It will often result in clunky, less efficient workarounds - and some features just won't be possible to implement at all that way.

This seems to indicate, as is so often the case, that we have a trade-off situation on our hands where we should try to put only just as much infrastructure code in our domain model classes as absolutely needed, but no more. We trade off some bloat in the domain model for some improved efficiency or enablement of some required Domain Model Management feature that wouldn't work otherwise. Negotiating good trade-offs is, after all, a large part of what software architecture is all about.

Time to Refactor

Unfortunately, the trade-off offered here might not be good enough to last you in the long run. The amount of infrastructure code that you will need to put in your domain model classes in order to support many of the most useful and powerful features is simply so big that your business logic code probably will drown before you have reached a production ready system.

That is, unless we can find a way to eat our cake and have it too. This article attempts to examine if we can find such a way to distribute the necessary infrastructure code over the domain model without actually littering the domain model classes themselves with any of this infrastructure code.

We will begin by looking at an application that puts all the relevant infrastructure code in the domain model classes. Then we are going to refactor the application, using only well known, tried and true object oriented design patterns, so that we end up with the same functionality as before but without littering the domain model classes with the infrastructure code. Finally we will look at how we can use Aspect Oriented Programming (AOP) to achieve the same effect in an even easier way.

But in order to see why AOP could be of any help to us when dealing with DMM requirements, we will begin by looking at how our code would look without it - first in the "rawest" form, where all the infrastructure code is placed in the domain model classes themselves and then in a refactored form where the infrastructure code has been factored out of the domain model classes - while still being distributed over the domain model!

Refactoring the Obese Domain Model

A lot of the runtime management of your domain model is based on interception - that is, as you access your domain model objects in your code, all your calls to the objects are intercepted by the features that so require.

An obvious example of this is dirty tracking. It could be useful to many parts of the application to know when an object has been modified but not yet saved (it is in a "dirty" state). The user interface could use this information to warn the user if they were about to discard any unsaved changes, whereas the persistence mechanism could use it to detect which objects actually require saving back to the persistent medium, thus avoiding having to save them all.

One way of doing this would be to keep copies of the original, unmodified versions of the domain objects around and to compare against them every time you wanted to know if an object had been modified. The problem with this scheme is that it is both wasteful of memory as well as slow. A more efficient approach would be to intercept all calls to the domain objects' setter methods so that whenever a setter method on an object is called, a dirty flag is raised for that object.

Where Do We Put The Dirty Flags?

We now run into the question of where to put the dirty flags. One option would be to place them in some dictionary structure, using the objects as keys and the flags as values. The problem with this is that we would then have to make that dictionary accessible to all parts of the application that might need it. We have already seen that this can include such different parts of the application as the user interface and the persistence mechanism.


Figure 1

Making the dictionary internal to either of these components would make it difficult to access for the others. In a layered structure, where lower layers cannot call higher layers (except for the central domain model which often resides in a shared, perpendicular layer that can be called by all other layers) it would necessitate putting it in either the lowest layer needing access to it (figure 1) or putting it in the shared, perpendicular layer (figure 2). Neither option is very attractive as it introduces undue coupling and an uneven distribution of responsibility between the components in your application.


Figure 2

A more inviting option, and one that goes well with object oriented thinking, would be to put the dirty flags on the domain objects themselves such that each domain object carries a Boolean dirty field indicating if it is dirty or not (figure 3). This way, any component that needs to know if a domain object is dirty can simply ask it.


Figure 3

So, part of the case for why we want to put some of the code for our infrastructure features in the domain model is that we want to be able to access these features from different parts of our application without unduly increasing the coupling. The UI shouldn't have to know how to ask the persistence component for the dirty flags and we prefer to invent as few perpendicular layers as possible in our layered application architectures.

This is an important reason, one that may be enough on its own for some to consider the approach we're about to examine in this article, but we will go on to see that there are others. But before we do so, let's take a glimpse at the other side of this argument - the reason why we may want to be restrictive about putting infrastructure code in our domain model classes.

The Obese Domain Model Anti-Pattern

Let's take a look at what a domain class might look like after you introduce a dirty flag as well as the interception required to raise it on the appropriate occasions. The example is in C# code.

public class Person : IDirty
{
protected string name;
public virtual string Name
{
get { return name; }
set
{
if (value != name)
((IDirty)this).Dirty = true;
name = value;
}
}

private bool dirty;
bool IDirty.Dirty
{
get { return dirty; }
set { dirty = value; }
}
}

public interface IDirty
{
bool Dirty { get; set; }
}
List 1

As can be seen from the example, the dirty flag is defined in an interface (IDirty) which is then implemented explicitly in the class. Explicit interface implementation is a nice C# feature that allows us to avoid cluttering the default API for the class with infrastructure related members.

This is helpful for example in the Visual Studio IDE where the Dirty flag will not show up in the code completion drop downs unless you explicitly cast to the IDirty interface. In fact, as can be seen in list 2, in order to access the Dirty property at all the object must first be cast to the IDirty interface.

The interception code in the example is limited to two lines of code (list 2). However, that's because we only have one property in our example so far. If we have more properties we'll have to repeat these two lines of code in each setter, each time modifying the comparison so that it is made against the appropriate field.

if (value != name)
((IDirty)this).Dirty = true;
List 2

So this wasn't too hard to write - and our domain model now supports dirty tracking in a way that is accessible to all components in the application that use the domain model and that is resource efficient and fast in that it doesn't require a copy of the unmodified version of the object being kept around.

The downside with this approach is that your domain model class is no longer strictly focused on the business concern it was meant to represent. Suddenly you have a lot of infrastructure code in it, making the actual business logic code harder to discern and the class itself more brittle, change-resistant and generally harder to maintain.

If dirty tracking were the only infrastructure feature that required (or could at least benefit from having) some infrastructure code inserted into the domain model classes, we might not be overly concerned. But this is unfortunately not the case. The list of such features goes on, but examples include the ones listed in table 1:

Dirty Tracking Object carries a Dirty flag showing if the object has been modified but not saved. Based on interception and introduction of new members (the dirty field and its getter and setter methods).
Lazy Loading Object state is not loaded until the first time the object is accessed. Based on interception and introduction of new members.
Original Value Tracking When a property is modified (but not saved) the object carries around a copy of the unmodified value. Based on interception and introduction of new members.
Inverse Property Management Property pairs representing a bi-directional relationship are automatically kept in synch. Based on interception.
Data Binding Object supports data binding interfaces and can thus be used in data binding scenarios. Based on introduction of new members (implementation of the data binding interfaces)
Replication Changes to objects are distributed to listening systems. Based on interception.
Caching Objects (or at least their state, see the Memento pattern [GoF Design Patterns]) are copied to local storage which is then queried instead of the remote data source in subsequent requests. Based on interception and introduction of new members.
Table 1

With all these features - and potentially more - relying on infrastructure code being added to your domain classes, the dream of a clean, maintainable domain model can suddenly seem remote.

I have described this problem, wherein the code in the domain model classes grows large enough as to become unwieldy, as the Obese Domain Model Anti-Pattern [Helander Obese Domain Model]. It provides a kind of counter-weight to the Anemic Domain Model Anti-Pattern [Fowler AnemicDomainModel] described by Martin Fowler, where the domain model classes contain too little logic.

In his description of the Anemic Domain Model, Fowler explains how it is a mistake - in fact, an anti-pattern - to put your business logic outside of the domain model, since it cannot then make full use of object oriented concepts and constructs.

I agree with his argument - I would even extend it to suggest that this goes just as much for the infrastructure code as for business logic code. The infrastructure code could also make use of OO concepts and constructs by being distributed over the domain model. By fiercely avoiding putting any infrastructure code in the domain model we therefore also run the risk of having this code fall prey to the issues associated with the Anemic Domain Model Anti-Pattern.

These limitations that spring from not allowing ourselves to add infrastructure code to the domain model - not fully utilizing OO - manifest in ways we have already seen: infrastructural features can become harder to share between different components in the application and we often end up with less efficient "workarounds", such as comparing against original values instead of doing interception based dirty tracking. There are even some features, such as lazy loading and inverse property management, which can not work at all without interception.

So, what is the solution, then? If we put all this infrastructure code in the domain model classes they become "obese" - hard to work with and maintain. But if we put it outside of the domain model classes, we get an "anemic" domain model where our infrastructure code can't make full use of the possibilities offered by object orientation.

We seem to be stuck between a rock and a hard place. Or, more specifically, we're stuck between an un-maintainable Obese Domain Model and a bunch of inefficient workarounds accompanying an Anemic Domain Model. This is obviously not a particularly nice place to be. It is time to address the age-old question: any ways to refactor ourselves out of this mess?

Using a Common Infrastructural Base Class

One idea would be to try to move as much as possible of this infrastructure code into a common base class which the domain model classes could then inherit from. The main problem with this, however, is that while it could be useful for introducing the new infrastructural members (such as the dirty flag) to the domain model classes, it doesn't provide us with the interception required for many of the features we want to support.

Another problem with the base class approach is that it may not provide the necessary level of granularity when it comes to controlling how features are applied. If different domain model classes have different infrastructure requirements we run into problems. We may be able to solve them by using a set of different base classes (perhaps inheriting from each other) but if we have single class inheritance, as is the case in C# and Java, we may not be able to do this if we also want to use inheritance in the domain model itself.

In order to see why this can become a problem, let's extend our example a bit to make slightly more "juicy": Let's say we should have a Person class which should have dirty tracking enabled and an Employee class which should have both dirty tracking and lazy loading enabled. Finally, the Employee class should inherit from the Person class.

If we were happy with putting our code right inside our domain model, we would now have a Person class looking like list 1 and an Employee class looking like list 3.

public class Employee : Person, ILazy
{
public override string Name
{
get
{
if (!loaded)
{
((ILazy)this).Loaded = true;

//call the persistence component
//and ask it to load the object
//with data from the data source
//(code omitted for brevity...)
}
return base.Name;
}
set
{
if (!loaded)
{
((ILazy)this).Loaded = true;

//perform lazy loading...
//(omitted)

}
base.Name = value;
}
}

private decimal salary;
public decimal Salary
{
get
{
if (!loaded)
{
((ILazy)this).Loaded = true;

//perform lazy loading...
//(omitted)

}

return salary;
}
set
{
if (!loaded)
{
((ILazy)this).Loaded = true;

//perform lazy loading...
//(omitted)

}

if (value != salary)
((IDirty)this).Dirty = true;

salary = value;
}
}

private bool loaded;
/// <summary>
///The Loaded property is "write-once" -
/// after you have set it to true you can not set
/// it to false again

///</summary>
bool ILazy.Loaded
{
get { return loaded; }
set
{
if (loaded)
return;

loaded = value;
}
}
}
List 3

As you can see, the bloat of infrastructure code threatening to drown the actual business concerns in the domain model classes has already started to creep upon us. This hint of the Obese Domain Model lurking in our future should motivate us to attempt a different approach - even if it may be one that demands a bit more effort up front. We do this since we hope a clean domain model will return that investment in maintainability and usability over the long term.

So, we begin by creating a DirtyBase base class which can provide the dirty flag and then we create a LazyBase base class which can provide a loaded flag. Working in a language with multiple implementation inheritance we could now let the Person class inherit from the DirtyBase class and the Employee could inherit from the DirtyBase and LazyBase classes, as depicted in figure 4.


Figure 4

But what if the language we want to use doesn't support multiple implementation inheritance? Well, the first thing we could do is to let LazyBase inherit from DirtyBase (see figure 5). That way we could still let the Employee class inherit the LazyBase class and still get both the dirty flag and the loaded flag. It might not be an optimal solution (what if we have an object that needs lazy loading but not dirty tracking?) but at least in this example case it could be an option.


Figure 5

However that still leaves us with the problem that the Employee class can't inherit from both the Person class and the LazyBase class in languages like C# and Java. The remaining option in these languages would be to create a base class containing both the dirty flag and the loaded flag (figure 6) and to let the Person class inherit a dirty flag it doesn't actually need.


Figure 6

So the base class approach has two problems: It doesn't provide the interception required for many features (dirty tracking and lazy loading included) and (at least on a single implementation inheritance platform) it leads to domain classes inheriting features they won't necessarily need.

Using Infrastructural Proxy Subclasses

Fortunately, OO offers an excellent way of killing both of these birds with just one stone. In order to provide both the interception and the granularity of control, all we have to do is to create subclasses which inherit from the domain model classes - one new subclass for every (concrete) domain model class.

The subclass can intercept the access to a member of its base class by overriding the member with a version that will begin by performing the interception related activities and then go on to pass on execution to the base class member.

In order to avoid having to create common infrastructure members (such as the dirty flag) in all the subclasses, they could still be put in a common base class that all the domain classes would inherit from, while features particular to a few classes could have the necessary members placed in the relevant subclasses (figure 7).


Figure 7

The common base class implements infrastructure members that are common to all domain model classes. Features that are not common to all domain classes get interfaces.

The domain model classes are clean from infrastructure code.

The proxy subclasses override domain class members to provide interception. They also implement infrastructure members that are not common to all domain classes.

Using a subclass in this way to provide interception is essentially an implementation of the Proxy pattern [GoF Design Patterns]. An alternative to using subclasses would be to use domain model interfaces - one per domain model class - which both the proxy class and the domain class could implement (figure 8).


Figure 8

The proxy class would then hold an instance of the domain class in an internal field and implement the domain interface by forwarding all calls to the hidden domain object. This is in fact the way the Proxy pattern is described in the original Design Patterns book by the Gang of Four [GoF Design Patterns] - but using subclasses is just as valid an approach.

The benefit of using subclasses is that you don't have to create a bunch of domain interfaces that might otherwise not be needed. There's also the advantage that code in a proxy subclass can call on code inherited from the domain class using the "this" keyword ("Me" in VB.NET) whereas the code in the interface based proxy will have to refer to the domain object via the internal field holding the reference to it. The subclass can also reach protected members that the interface based proxy would have to reach by reflection.

An additional twist on this issue is that with an interface based proxy, if you have a method in the domain class that returns a reference to "this", the effect will be that an "unproxied" instance of the domain object is returned to the calling code, meaning that features such as dirty tracking and lazy loading won't work on the returned instance.

Because of these issues I personally prefer the subclass based proxy approach, and this is the method we will continue to look at throughout this article. However, please keep in mind that all the techniques that will be discussed in this article could be implemented just as well using the interface based proxy approach.

The POJO/POCO Domain Model

If we compare the Proxy pattern based approach just discussed to our earlier example code in C#, our domain classes can now become completely clean, free of all infrastructure code, as can be seen in the beginning of list 4. All the infrastructure code has been moved to the base class and the proxy subclasses, shown at the end of the list.

//Domain Model Classes
public class Person : DomainBase
{
protected string name;
public virtual string Name
{
get { return name; }
set { name = value; }
}
}

public class Employee : Person
{
protected decimal salary;
public virtual decimal Salary
{
get { return salary; }
set { salary = value; }
}
}

//Infrastructure Base Class
public class DomainBase : IDirty
{
private bool dirty;
bool IDirty.Dirty
{
get { return dirty; }
set { dirty = value; }
}
}

//Infrastructure Proxy Subclasses
public class PersonProxy : Person
{
public override string Name
{
get { return base.Name; }
set
{
if (value != this.name)
((IDirty)this).Dirty = true;
base.Name = value;
}
}
}

public class EmployeeProxy : Employee, ILazy
{
public override string
{
get
{
if (!loaded)
{
((ILazy)this).Loaded = true;

//call the persistence component
//and ask it to load the object
//with data from the data source
//(code omitted for brevity...

}
return base.Name;
}
set
{
if (!loaded)
{
((ILazy)this).Loaded = true;

//perform lazy loading...
//(omitted)

}

if (value != this.name)
((IDirty)this).Dirty = true;

base.Name = value;
}
}

public override decimal Salary
{
get
{
if (!loaded)
{
((ILazy)this).Loaded = true;

//perform lazy loading...
//(omitted)

}
return base.Salary;
}
set
{
if (!loaded)
{
((ILazy)this).Loaded = true;

//perform lazy loading...
//(omitted)

}

if (value != this.salary)
((IDirty)this).Dirty = true;

base.Salary = value;
}
}

private bool loaded;
/// <summary>
/// The Loaded property is "write-once" -
/// after you have set it to true you can not set
/// it to false again

/// </summary>
bool ILazy.Loaded
{
get { return loaded; }
set
{
if (loaded)
return;

loaded = value;
}
}
}
List 4

By employing the Proxy pattern, potentially in combination with a common base class, we can successfully address all the issues we have looked at and which have so far taunted us either from the direction of the Anemic or the Obese Domain Model Anti-Pattern:

  • We are able to distribute relevant parts of our infrastructure over the domain model so that it is easy to access by all parts of our application and so that it can be implemented in an object oriented and efficient manner.
  • We are able to distribute as much infrastructure code as we like over the domain model without running the risk of ending up with an Obese Domain Model. In fact, the code in our actual domain model classes can remain totally clean, focusing entirely on the business aspects they are trying to capture.
  • We can mix and match, adding only the infrastructure code required to each domain model class.
  • We can build features that are based on interception.

It should be noted that using a strict definition of POJO/POCO, our domain model classes should not inherit from an infrastructural base class. However, we can avoid this by moving all of the logic from the common base class to the proxy subclasses - that way we end up with a completely POCJO/POCO domain model. If that is a requirement than it is easily realizable with our approach, it is just a bit more work. If it is not a requirement we can use the base class and still see that the code in our domain model classes is completely free of actual infrastructure code.

So, at this point we have an architecture that allows us to combine completely POJO/POCO domain model classes with the goal of distributing our infrastructure code over the domain model. We can also take the "semi-POJO/POCO" route of using a common base class, saving ourselves some work if our goal is merely to avoid an obese domain model and not necessarily fulfill the definitions of POJO/POCO.

Using the Abstract Factory Pattern

This sounds wonderful, right? Are there no issues at all with this approach, then, you may wonder. Since the software business can make people cynical, chances are you will actually suspect a nasty problem or two to rear their ugly heads, right about now.

And you'd be right. Two matters for concern do present themselves immediately. The first one is fairly minor: In order for the subclasses to be able to override the members of the domain model classes and provide interception, all the domain model members must be made virtual (or at least all the ones you want to be able to intercept).

The second issue is potentially worse: Since you want your application to work with instances of your proxy subclasses, you must visit all the places in your application code where you create a new instance of one of your domain model classes and modify it so that an instance of the relevant proxy subclass is created instead.

If that sounds like half a nightmare plus change to do in an already existing application, well, then that's because it is. There is only one way to avoid this massive search and replace operation and that is to avoid using the "new" keyword for creating instances of your domain model classes from the very beginning.

A traditional way of avoiding "new" is to make use of the Abstract Factory pattern [GoF Design Patterns]. Instead of letting client code create instances using "new" they call a Create() method on a Factory class. The Create() method is responsible for making the call to "new" and it may also do additional setup related operations to the new instance before returning it.

The wonderful part if you have used the Abstract Factory pattern throughout in your calls to the domain model is that you will then be able to modify just one place in your code - the Factory class - changing it from returning instances of the domain classes to returning instances of the proxy subclasses (or interface based proxies for that matter).

This is a heavy argument for using the Abstract Factory pattern for all your domain object instantiation - at least in languages such as Java and C# that don't allow exotic features such as overloading the member access operator like C++, changing the behavior of the "new" keyword like ObjectiveC or modifying classes at runtime like Ruby.

Reflecting On Inheritance

There is one more issue worth mentioning with the Proxy subclass approach. While it is really a corner case scenario, it is fairly subtle and can therefore bite you bad if you do run into it without knowing what's causing it.

If you take a look at figure 9 you'll notice that the Employee class inherits from the Person class, just as it should: whenever a method expects a Person object, passing an Employee object should work just as well. Furthermore, the PersonProxy class inherits from the Person class. This is also good because that means that it is "legal" to pass a PersonProxy object as a parameter to a method expecting a Person object.


Figure 9

In the same way, the EmployeeProxy inherits from Employee, meaning that you can pass an EmployeeProxy object to any method expecting an Employee object. Finally, since EmployeeProxy inherits from Employee and Employee inherits from Person, this means EmployeeProxy inherits from Person. Thus, any method expecting a Person object will also accept an EmployeeProxy object.

All this is just as we would hope and expect. It is important to us that when our Abstract Factory suddenly starts returning proxy objects rather than plain instances of the domain model classes our client code continues to function as normal, without having to rethink they way our type hierarchies need to be handled.

In other words, if we have a method expecting a Person object to which we used to (legally) pass an Employee object, our code must continue to compile (and work) when we suddenly pass it an EmployeeProxy object instead. Fortunately, since EmployeeProxy inherits from Employee, and Employee inherits from Person, this will continue to work fine.

In fact, everything will continue to function just dandy when you start using the proxies instead of the domain model objects, just as one would hope. Everything, that is, except for one tiny little thing.

The red inheritance line in figure 9 is meant to symbolize that EmployeeProxy does not inherit from PersonProxy. When might this present a problem?

Well, consider a method that accepts two objects and that proceeds to use reflection to determine of one of the objects belongs to a subclass of the other object. When we passed a Person object and an Employee object to this method, it would return true. But when we pass a PersonProxy object and an EmployeeProxy object to the method, it would suddenly return false.

Unless you have reflection based code that inspects inheritance hierarchies, you should be safe. But in case you do, it doesn't hurt to be aware of this caveat (which you will then be able to get around by modifying your reflection code so that it detects proxies and steps up the type hierarchy until it finds an unproxied type).

Using Mixin and Interceptor Classes

We have come a long way towards a maintainable domain model architecture that will allow for a capable, efficient and accessible infrastructure for runtime Domain Model Management.

Could we do even more?

Let's begin by taking a look at those features that should be applied only to some domain classes but not to all - the features where we need to put the required infrastructure members in the proxy subclasses rather than in the shared base class. In our example the loaded flag for the lazy loading feature will have to be added to the subclass of every class that needs to support lazy loading.

Because we potentially need to put the same code in multiple subclasses, we run the obvious risk of ending up with code duplication. In the case of just one flag this may not be too bad, but what about a feature that needs several fields and perhaps even methods with complicated implementations?

In our example the loaded flag is write-once, so that after it has been switched to true it can't be switched back. Accordingly we have some code in the setter method to enforce this rule - code which we would need to repeat in the setter method for the loaded flag in the subclass of each and every domain model class that needed lazy loading.

What we would like to do is to create a reusable class containing this logic. But we have already used up the inheritance (still assuming we work on a single inheritance platform) so how can this lazy loader class be reused?

One good answer is to use the Composite pattern [GoF Design Patterns] rather than inheritance for this. Using this pattern, the EmployeeProxy subclass would contain an internal field with a reference to an instance of the reusable lazy loader class (figure 10).


Figure 10

The reusable class is often referred to as a mixin, reflecting the fact that implementation is being mixed in to the type rather than being part of the inheritance hierarchy. The effect of using mixins is a lot like being able to use multiple implementation inheritance in a single implementation inheritance language (or even on a platform that only supports interface inheritance, such as COM+).

An interesting side note to this discussion is that using our composition approach, behavior and state in the form of mixins can be dynamically mixed in to target classes (using Dependency Injection) rather than being statically bound to targets (as would be the case if the behavior in question were inherited). While we won't look more at how this potential can be exploited in this article, it opens up a wide range of opportunities for supporting very flexible and dynamic scenarios.

By breaking out the members introduced by the subclass into a reusable mixin class we take one further step towards a truly modular, coherent and cohesive architecture with low coupling and high potential for code reuse. Can we go even further in this direction?

Well, the next thing to do would be to break out the interception code from the subclasses and put that as well into reusable classes. These interceptor classes would be contained by the proxy subclasses in the same way as the mixins, as shown in figure 11.


Figure 11

The Person, Employee and DomainBase classes remain unchanged since the last version of the code, but the PersonProxy and the EmployeeProxy classes have changed, and we have introduced two new interceptor classes and one new mixin class. List 5 shows how the code looks after we have performed these refactorings (excluding the unmodified classes).

//These proxy subclasses contain only boilerplate
//code now. All actual logic has been refactored
//out into mixin and interceptor classes.

public class PersonProxy : Person
{
private DirtyInterceptor
dirtyInterceptor = new DirtyInterceptor();

public override string Name
{
get { return base.Name; }
set
{
dirtyInterceptor.OnPropertySet(this, this.name, value);
base.Name = value;
}
}
}

public class EmployeeProxy : Employee, ILazy
{
//This mixin contains the implementation
//of the ILazy interface
.
private ILazy lazyMixin = new LazyMixin();

private LazyInterceptor lazyInterceptor = new LazyInterceptor();
private DirtyInterceptor dirtyInterceptor = new DirtyInterceptor();

public override string Name
{
get
{
lazyInterceptor.OnPropertyGetSet(this, "Name");
return base.Name;
}
set
{
lazyInterceptor.OnPropertyGetSet(this, "Name");
dirtyInterceptor.OnPropertySet(this, this.name, value);
base.Name = value;
}
}

public override decimal Salary
{
get
{
lazyInterceptor.OnPropertyGetSet(this, "Salary");
return base.Salary;
}
set
{
lazyInterceptor.OnPropertyGetSet(this, "Salary");
dirtyInterceptor.OnPropertySet(this, this.name, value);
base.Salary = value;
}
}

//The ILazy interface is implemented
//by forwarding the calls to the mixin,
//which contains the actual implementation
.
bool ILazy.Loaded
{
get { return lazyMixin.Loaded; }
set { lazyMixin.Loaded = value; }
}
}

//The following mixin and interceptor classes
//contain all the actual infrastructural logic
//associated with the dirty tracking and
//the lazy loading features.

public class LazyMixin : ILazy
{
private bool loaded;
/// <summary>
/// The Loaded property is "write-once" -
/// after you have set it to true you can not set
/// it to false again

/// </summary>
bool ILazy.Loaded
{
get { return loaded; }
set
{
if (loaded)
return;

loaded = value;
}
}
}

public class DirtyInterceptor
{
public void OnPropertySet(
object obj,
object oldValue,
object newValue)
{
if (!oldValue.Equals(newValue))
{
IDirty dirty = obj as IDirty;
if (dirty != null)
dirty.Dirty = true;
}
}
}

public class LazyInterceptor
{
public void OnPropertyGetSet(object obj)
{
ILazy lazy = obj as ILazy;
if (lazy != null)
{
if (!lazy.Loaded)
{
lazy.Loaded = true;

//perform lazy loading...
//(omitted)

}
}
}
}
List 5

By refactoring so that all the actual infrastructure logic ends up in mixins and interceptor classes, the proxy subclasses turn into fairly slim, lightweight classes that focus on forwarding calls to the interceptors and mixins. In fact, at this point the proxy subclasses consist of nothing more than boilerplate code that could easily be generated by a code generation tool.

One option would be to generate the code for the proxy subclasses at design time, but languages like C# and Java allow that you generate, compile and execute code at runtime. Therefore we also have the option of generating the proxy subclasses at runtime, a practice that is commonly known as runtime subclassing.

Take a moment to reflect over how far we have come. In the first step we were able to refactor our way from an Obese Domain Model to a POJO/POCO Domain Model using proxy subclasses (and, optionally, a base class) in such a way that the infrastructure code could still be distributed over the domain model. In the second step we refactored all the actual infrastructural logic away from the proxy subclasses and into reusable mixin and interceptor classes, leaving only boilerplate code in the proxy subclasses.

In the final step we turn towards generating the full code for the boilerplate proxy subclasses using a runtime code generator. And at this point we have come all the way to the land of Aspect Oriented Programming.

Using Aspect Oriented Programming

For the longest time I felt curious about all the AOP hoopla only to become completely mystified every time I tried to read up on what it was all about - mainly because of the insistence by the AOP community on using such weird terminology. It also doesn't help that it often feels like some (not all!) AOP advocates seem to go out of their way to make the subject appear excitingly different and, well, intimidating.

Thus it may come as a surprise to you to learn that if you have read this far, you have in fact already grasped all the major concepts of Aspect Oriented Programming - and you've been given an idea about how an AOP framework can work under the hood, to boot.

Aspect Oriented Programming uses the central concept of an aspect, which is simply a collection of introductions (mixins) and advice (interceptors). The aspect (the interceptors and mixins) can be applied to your existing classes at runtime using, among other possible techniques, runtime subclassing.

As you can see, you already have an understanding of most of the weird but important AOP terms. The term crosscutting concerns that is used a lot to describe what aspects are good for simply means that you have a feature that should be applied to a lot of classes (that don't necessarily share an inheritance hierarchy) - such as our dirty tracking and lazy loading features which are concerns that are crosscutting across the domain model.

The only really important AOP terms we haven't really covered so far are join points and pointcutting. A join point is a place in a target class to which you can apply an interceptor or a mixin. Normally the join points of interest for interceptors are the methods while the join points of interest for mixins are the classes themselves. A join point is, in a nutshell, a point where you can join in your aspects.

In order to define which aspects that should be applied to which join points, you use a pointcutting language. This language can be something simple such as a declarative language where you might just name aspect and join point combinations in an xml file, but can sometimes also allow for more advanced "pointcutting" (targeting of join points) using regular expressions or even fully blown Domain Specific Languages.

So far, pointcutting has not been a concern for us since we have manually applied our aspects (our mixins and interceptors) exactly where we wanted them. But if we want to take the final step to using an AOP framework for applying our aspects we need to consider this subject as well.

But before we do, let's pause to ponder what is being proposed. Using an AOP framework at this point, after the refactorings we have done, is actually not at all a drastic step. It is a very small step, since we have already effectively refactored our application into using aspects in the form of interceptors (advice) and mixins (introductions).

In fact, I would suggest that at this point we are in effect using AOP - we're just not using an AOP framework to automate the boilerplate parts for us yet. But we are using all the essential concepts from AOP (interception and mixins) and we're doing it in a reusable way addressing crosscutting concerns. That sounds like the very definition of AOP, so it shouldn't be surprising that an AOP framework could help us automate some of the work we'd otherwise have to do manually.

The code we currently have in our interceptor and mixin classes can be minimally modified to work as aspects - it has already been refactored into a generic, reusable form which is just how aspects are supposed to be written. The only thing we would need the AOP framework to do is to help us apply our aspects to the domain model classes in some way. As mentioned, there are many ways to do this for the AOP framework, but one way is to generate proxy subclasses at runtime.

In the case of a runtime subclassing framework, the code for the generated proxy subclasses will look a lot like the boilerplate code in the proxy subclasses in our example. In other words, it is easily generated. If you think you could write a code generator up to the task, you're halfway towards implementing the engine for your very own AOP framework.

Refactoring to Aspects

In our final refactoring, we will take a look at how our example application will look if we use an AOP framework to apply our interceptors and mixins. In this example I will use NAspect, the open source AOP framework for .NET that Roger Johansson and I have written together. NAspect uses runtime subclassing to apply the aspects.

Having read thus far you'll have no trouble understanding the "magic" that NAspect performs - it simply generates a proxy subclass with the boilerplate code required for forwarding calls to mixins and interceptors and it uses standard object oriented overriding of domain class members to provide the interception.

Many people associate AOP with voodoo magic of the worst kind. If the bizarre terminology is not enough to discourage them, the way things happen suddenly and "invisibly" in interceptors without the developer being able to see it in the domain class code will strike many as deeply discomforting, almost offensive.

It helps to get a concrete understanding of what the terms mean, of course. It also helps to understand at least one way that the aspects can be applied and to see that it isn't really a question of magic - just a few good old, well known, object oriented design patterns at work.

AOP is all about using well known OO patterns in a way that lets us address cross cutting concerns. An AOP framework is just a thing that lets you apply your aspects more comfortably than by having to manually write a lot of proxy subclasses yourself.

So, now that we're not intimidated by AOP anymore, let's look at how we can use it to bring our application that final step to the goal line.

The first thing we do, in this case, is to include a reference to the NAspect framework assembly. When we have that in place, we can begin creating our aspects and defining our pointcuts. We can also go ahead and delete our PersonProxy and EmployeeProxy classes - from now on, NAspect will generate those classes for us.

We can even delete the DomainBase base class if we like - there's no need to use a base class to apply mixins that are common to all the base classes, we might as well do that too using the AOP framework. This means that by using an AOP framework we're able to meet even the strict requirements of POJO/POCO without any additional work.

In order to get rid of the base class, we just move the functionality into mixins. In our case we just need to create one more mixin - the DirtyMixin, which holds the dirty flag that previously resided in the base class.

The code in the LazyMixin class can remain entirely unmodified so it will not be listed. In fact, all we really need to do is to create the new DirtyMixin class and to modify the code in the two interceptor classes slightly, to make them work with the data structure representing the intercepted method which is passed to them by NAspect.

We also need to modify our factory class so that it uses NAspect to create proxy subclasses for us. This is assuming we have already gone over to using the Abstract Factory pattern everywhere in our application. If we had not yet done so, we would certainly learn our lesson now since without the Abstract Factory pattern, we would have to undertake another huge search and replace operation, going over each place in the code where proxy subclasses were being instantiated and changing to calls to the NAspect runtime subclassing engine.

The refactored code, ready for use with NAspect, is shown in list 6.

public class DirtyMixin : IDirty
{
private bool dirty;
bool IDirty.Dirty
{
get { return dirty; }
set { dirty = value; }
}
}

public class DirtyInterceptor : IAroundInterceptor
{
public object HandleCall(MethodInvocation call)
{
IDirty dirty = call.Target as IDirty;

if (dirty != null)
{
//Extract the new value from the call object
object newValue = call.Parameters[0].Value;

//Extract the current value using reflection
object value = call.Target.GetType().GetProperty(
call.Method.Name.Substring(4)).GetValue(
call.Target, null);

//Mark as dirty if the new value is
//different from the old value

if (!value.Equals(newValue))
dirty.Dirty = true;
}

return call.Proceed();
}
}

public class LazyInterceptor : IAroundInterceptor
{
public object HandleCall(MethodInvocation call)
{
ILazy lazy = call.Target as ILazy;

if (lazy != null)
{
if (!lazy.Loaded)
{
lazy.Loaded = true;

//perform lazy loading...
//(omitted)

}
}

return call.Proceed();
}
}

public class Factory
{
public static IEngine engine = ApplicationContext.Configure();

public static Domain.Person CreatePerson()
{ return engine.CreateProxy<Domain.Person>();
}

public static Domain.Employee CreateEmployee()
{
return engine.CreateProxy<Domain.Employee>();
}
}
List 6

In order to know which classes to apply your aspects to, NAspect uses (among other options) an xml configuration section in the application configuration file. At this point we could define our aspects in xml by pointing out the types of our mixin and interceptor classes as well as the types we want to apply them to. In principle, we are ready to try out our AOP application.

However, there is one thing that can still make many developers hesitant about using AOP, even when they understand the terminology and the theory behind AOP well, and that is the fragility that comes with a weakly defined pointcutting system.

While we could define a few clever regular expressions that would let us target just the classes and members we wanted to apply our aspects to, chances are you would forget to update your pointcut specifications when your domain model changes and so suddenly your regular expression would target classes that the aspects should not be applied to. This is just the type of thing that gives AOP a bad reputation as a voodoo technology.

One way of dealing with this pointcutting imprecision is by creating custom .NET Attributes (annotations in Java). By decorating the domain model classes with custom attributes and then using the attributes as pointcut targets we avoid the hocus pocus of using regular expressions and the like for applying our aspects. Aspects are only applied where we consciously decide to apply them by putting attributes there.

So in our case, we go on by creating two custom attributes - LazyAttribute and DirtyAttribute - that we then use to decorate our classes (list 7).

public class DirtyAttribute : Attribute
{
}

public class LazyAttribute : Attribute
{
}

[Dirty]
public class Person : DomainBase
{
protected string name;
public virtual string Name
{
get { return name; }
[Dirty]
set { name = value; }
}
}

[Dirty]
[Lazy]

public class Employee : Person
{
protected decimal salary;
public virtual decimal Salary
{
[Lazy]
get { return salary; }
[Dirty]
[Lazy]

set { salary = value; }
}

public override string Name
{
[Lazy]
get { return name; }
[Dirty]
[Lazy]

set { name = value; }
}
}
List 7

We finally go on to define our pointcuts in the application configuration file (list 8) so that our aspects target our custom attributes.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>

<section
name="naspect"
type="Puzzle.NAspect.Framework.Configuration.NAspectConfigurationHandler, Puzzle.NAspect.Framework.NET2"/>

</configSections>

<!-- Puzzle.NAspect.Framework settings -->
<naspect>
<configuration>

<aspect
name="DirtyAspect"
target-attribute="InfoQ.AspectsOfDMM.Attributes.DirtyAttribute, InfoQ.AspectsOfDMM" >

<pointcut
target-attribute="InfoQ.AspectsOfDMM.Attributes.DirtyAttribute, InfoQ.AspectsOfDMM" >

<interceptor
type="InfoQ.AspectsOfDMM.Aspects.DirtyInterceptor, InfoQ.AspectsOfDMM" />

</pointcut>

<mixin type="InfoQ.AspectsOfDMM.Aspects.DirtyMixin, InfoQ.AspectsOfDMM"/>

</aspect>

<aspect
name="LazyAspect"
target-attribute="InfoQ.AspectsOfDMM.Attributes.LazyAttribute, InfoQ.AspectsOfDMM" >

<pointcut
target-attribute="InfoQ.AspectsOfDMM.Attributes.LazyAttribute, InfoQ.AspectsOfDMM" >

<interceptor
type="InfoQ.AspectsOfDMM.Aspects.LazyInterceptor, InfoQ.AspectsOfDMM" />

</pointcut>

<mixin type="InfoQ.AspectsOfDMM.Aspects.LazyMixin, InfoQ.AspectsOfDMM"/>

</aspect>

</configuration>
</naspect>
</configuration>

We're now finally ready to try out our AOP application. When we run the application, NAspect will generate proxy subclasses for us at runtime and insert the boilerplate code in the subclasses to forward all calls to your interceptor and mixin classes.

The final architecture for our application can be seen in figure 12. Note that the only actual difference between this version and our earlier, non-AOP version, is that the two proxy classes (marked with broken outlines) will now be generated by the AOP framework, whereas before we had to code them manually.

Compared to figure 11 we have an additional DirtyTrackerMixin class and a new IDirtyTracker interface replacing the DomainBase base class, but this is just what we would have had without using AOP as well, had we decided to go without the common infrastructural base class (meeting stricter POJO/POCO requirements). In other words, if we don't want to use a common base class, we end up with exactly the same architecture (the one shown in figure 12) regardless of whether we use an AOP framework or not.

Figure 12

Pointcutting Options

When you add new domain model classes to the mix, all you need to do in order to enable lazy loading and dirty tracking for them is to decorate them with the custom attributes and the features will be applied to them "automagically".

sing attributes/annotations as pointcutting targets, you'll quickly note that using one attribute per feature that you want to apply has a tendency of producing an awful lot of attributes per member in your domain model. Thus, to reduce the number of attributes you might want to look for abstractions between them.

Another option, if you feel comfortable with a regular expression based pointcutting approach and you feel that littering the domain model with attributes compromises the goal of keeping it completely free of all infrastructure concerns, you can just configure your xml configuration file to match the relevant target classes and will not need to decorate your domain classes with custom attributes.

Alternatively, the xml pointcutting language of NAspect (as of many other AOP frameworks) allows you to simply supply a full list of the types that an aspect should apply to, avoiding the need to find a regular expression that somehow matches just the right classes. This makes for a more verbose configuration file, but allows your domain model to remain completely clean.

An additional benefit from using an AOP framework where you use pointcutting to apply your aspects dynamically is that it becomes easy to apply different aspects to the same classes when they are used in different use cases. If one use case requires lazy loading for a certain domain class while another use case doesn't, only the first use case would have to use a configuration file that applies the lazy loading aspect.

This dynamic application of aspects can be very powerful, not least in testing scenarios where additional testing aspects can be applied, for instance to provide mocking capabilities.

Conclusions from using Aspect Oriented Programming

We started with the question of whether we were allowed to put any of our infrastructure code in the domain model. We saw that this was certainly desirable since it allowed for more efficient implementation of many features, but that it threatened to make our domain model "obese".

To solve this issue we refactored our obese domain model - first by moving the infrastructure code into proxy subclasses and a base class, then by moving the actual logic out of those proxy subclasses and into mixins and interceptor classes. This made our domain model buff but we ended up with a lot of boilerplate code to write in the proxy subclasses. In order to solve this we turned to Aspect Oriented Programming.

The step to using an AOP framework turned out to be so small - in fact our application architecture didn't change one bit - that it probably came as a surprise to some readers, who may even experience that familiar feeling that often comes when you understand a design pattern for the first time and you realize it is something you've done often but without knowing it had a name.

You may well have effectively been doing AOP in many of your applications without realizing it and without, as it were, using an AOP framework to help you automate some of the boilerplate code generation.

We are now at the end of this article and hopefully at this point you'll agree with me that:

  • AOP isn't as hard or confusing as it looks, and using an AOP framework is easy.
  • Regardless of whether you use an AOP framework, using AOP concepts and modeling techniques is still a great way for handling the crosscutting Domain Model Management concerns in your application infrastructure.

It's really all just a matter of refactoring your way towards steadily improving your application architecture, using good old fashioned OO patterns. When doing so, don't be afraid to move towards an application architecture that could be described as Aspect Oriented, using proxies to apply interceptors and mixins.

Whether you then go on to let an AOP framework help you with applying your aspects doesn't really affect weather you're doing AOP or not. Because, if you are refactoring your application along the lines discussed in this article, you are doing AOP - with or without the help of a framework.

Summary

By refactoring our Obese Domain Model using traditional, well known object oriented design patterns such as the Proxy pattern, the Abstract Factory pattern and the Composite pattern, we arrived at an application architecture that elegantly combines a lean domain model with an infrastructure that can take full advantage of object oriented concepts and constructs. This way, we neatly avoided falling in neither of the Obese Domain Model nor the Anemic Domain Model Anti-Pattern traps.

Employing the patterns in the way discussed in this article leads to an application architecture that could well be described as Aspect Oriented. This is because we end up with mixin and interceptor classes that address our crosscutting concerns, closely matching the introduction and advice concepts found in Aspect Oriented Programming.

If you want to see for yourself that using AOP is really this fun and easy, but you wouldn't mind not having to type in all the code, you can download the full code from this article in the accompanying Visual Studio 2005 project.

In summary, in this article I have tried to show how Aspect Oriented concepts and tools can provide a great way of thinking about and applying many of your Domain Model Management concerns and how they help greatly in mitigating the risk of the Obese Domain Model Anti-Pattern.

About the author

Mats Helander works for Avanade Netherlands as a Senior Software Development consultant. In his free time he has developed the Puzzle.NET suit of free, open source frameworks and tools together with Roger Johansson. Puzzle.NET includes NPersist and ObjectMapper (O/R Mapping), NAspect (AOP), NFactory (Dependency Injection) and NPath (in memory object querying).

Mats Helander's Weblog - http://www.matshelander.com/wordpress
Puzzle.NET - http://www.puzzleframework.com
Avanade Netherlands - http://www.avanade.com/nl/

References

[Evans DDD]
Evans, Eric. Domain Driven Design: Tackling Complexity in the Heart of Software. Boston, MA: Addison-Wesley, 2004.

[Fowler AnemicDomainModel]
Fowler, Martin. http://martinfowler.com/bliki/AnemicDomainModel.html

[Fowler PoEAA]http://martinfowler.com/bliki/AnemicDomainModel.html Fowler, Martin. Patterns of Enterprise Application Architecture. Boston, MA: Addison-Wesley, 2003.

[GoF Design Patterns]
Gamma, Erich, Richard Helm, Ralph Johnson, and John M. Vlissides, Design Patterns: Elements of Reusable Object-Oriented Software. Reading, MA: Addison-Wesley, 1995.

[Helander DMM]
Helander, Mats. http://www.matshelander.com/wordpress/?p=30

[Helander Obese Domain Model]
Helander, Mats. http://www.matshelander.com/wordpress/?p=75

[Nilsson ADDDP]
Nilsson, Jimmy. Applying Domain-Driven Design and Patterns. Addison-Wesley, 2006.


You can find the source code here.

Hello stranger!

You need to Register an InfoQ account or or login to post comments. But there's so much more behind being registered.

Get the most out of the InfoQ experience.

Tell us what you think

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

Email me replies to any of my messages in this thread
Community comments

Bugs in article by Mats Helander

Even though I proof-read the article several times before submission, I'm still able to spot a few bugs in it now. I will continue to update a blog post for this article at the following address with bugs as I find them:

www.matshelander.com/wordpress/?p=80

I will still list the bugs I have found so far at the end of this comment.

Also, while this may be my mistake, I don't see the link to the code? At any rate, you can download the Visual Studio project with the code for the article here:

www.matshelander.com/infoq/Infoq.AspectsOfDMM.zip

Thanks,

/Mats

List of Bugs:

List 2

The Persistence component should not contain any dirty flags.

Figure 6 (text above)

The text states that the person class inherits a dirty flag it doesn’t need, but it should be that it inherits a lazy flag that it doesn’t need.

Figure 7, 10, 11, 12

The EmployeeProxy class should have getName and setName methods (just like the PersonProxy class) overriding the ones on the Employee class in order to provide the interception.

List 4

The first property in the EmployeeProxy class misses its name. Currenlty it just says “public override string”, it should be “public override string Name”.

List 5

In the setter method for the Salary property in the EmployeeProxy class, the call to the OnPropertySet method on the dirtyInterceptor sends “this.name” as the second parameter – it should be “this.salary”.

All the calls to the lazyInterceptor send along a second parameter with the property name – but the interceptor method only accepts one parameter (and doesn’t need a second one). Thus all the calls to the lazyInterceptor should be changed to: “lazyInterceptor.OnPropertyGetSet(this);”

List 7

The Person class should no longer inherit from the DomainBase class (which has been deleted).

Nice article ! by Joel Costigliola

Hi Mats,

Very nice article, describing well a way to get a clean OO design, and introducing how AOP can help !

I have spotted some mistakes (no big deal, the article is easy to read anyway), concerning list 5 code not completely faithful to figure 11.

Figure 11 / List 5
- ILazyLoader / ILazy
- LazyLoaderMixin / LazyMixin
- LazyLoadingInterceptor / LazyInterceptor

Well as I said, no big deal, just some "lazy" mistakes ;o)

Regards,

Joel

Re: Nice article ! by Mats Helander

Hi Joel,

Thanks for the kind words and for the bug spottings! :-)

/Mats

Re: Bugs in article by Diana Baciu

Hi Mats
i've added a link to the code at the end of the article.

diana

Great article! by Steve Macdonald

Mats:

This was one of the most useful articles I've come across in a while. I've circulated it amongst my peers. There is a real need for people like you who can communicate these critical ideas so concisely and clearly. In our shop we basically live and breathe Evans, Fowler and Nilsson. Looks like we'll be adding Helander to that list!

Happy New Year!

Steve

Re: Great article! by Mats Helander

Wow...thanks Steve! I really don't know what to reply to such kind words, I'm all embarrased now :-P

Reading your comment certainly made a great start on my new year! A very Happy New Year to you as well! :-)

/Mats

Good article by Kristof Jozsa

Hi Mats,

having read your article word-by-word, I'd say good work. Please allow me some constructive comments.

The article [imho] is about 4 times longer than it should be based on the content. I also couldn't help thinking "omg, this guy's crazy" at least 5 times during reading the article (especially around introducing subclass hell with code examples and claiming it's a good approach). If the article's title or introduction warned me about showing evolutionary thinking to introduce AOP, you could have spare this :) My bet would be that many readers left in the middle of the article thinking you have no idea at all about OO design.. Anyway, nice work indeed!

Well Done! by Perry Hertler

Thanks for taking the time to write this great article, which lays a good foundation for AOP concepts while showing the problem it solves.

One comment: In the real world the Dirty and Lazy mixins would almost certainly need to be thread safe.

--
Perry

Re: Good article by Mats Helander

Hi Kristof,

Thanks for the feedback,

Sorry I wasted your time...I assure you that if I knew how to write more succinctly, I would! ;-) But even though the title perhaps didn't reflect this, I think I did explain in the introduction what would come (a refactoring to aspects)? But you are certainly right that it's a long text and if I didn't do a good enough job of giving the reader accurate expectations of the content from the introduction, then that is indeed a severe issue, so thanks for pointing it out!

I googled "subclass hell" to see if this was a widespread opinion with its own term, but only got one hit with that usage. I'm not sure I know what you mean by the term but I assume you mean "getting an explosion of subclasses" kind of thing? Again, as was the point of the article, this is only an issue (if even then) should you decide to code your subclasses manually, which is not necessary at all - they can be easily generated at design time or runtime. But I would love some more info on this perspective - for example, if a bunch of subclasses is considered more "hellish" than an obese domain model (bloated domain classes) or having to use inefficient workarounds such as using original values to get dirty status - and if so, why? Do you have any links to discussions attacking the architecture I suggest on the basis of the proliferation of subclasses?

"My bet would be that many readers left in the middle of the article thinking you have no idea at all about OO design"

Given your objections above ^^ (subclassing and some other instances of thinking I'm crazy (that I'd love to know more about as well)) I didn't quite understand if this was your opinion as well (that I have no idea about OOD) or if you think the suggestions I make in the article aren't actually crazy - only somewhat poorly explained?

/Mats

Re: Well Done! by Mats Helander

Hi Perry,

Thanks for the nice words. :-)

You're absolutely right about the thread safety of course and I should have pointed it out in the article - great point, thanks!

/Mats

Re: Good article by Kristof Jozsa

Hi Mats,

you didn't waste my time, I just thought this content can be explained in a more compact form :) I don't think 'subclass hell' is a wide-spread known term but I guess you got what I meant: the sperodic grow of number of subclasses around a project without a business reason. Generally I believe good OO design mostly prefers composition in favour of subclassing. (I guess it's worth to mention that by 'subclassing' I mean the 'manual' way, subclasses generated by an AOP framework wouldn't count this way (it's rather a technical solution not subclass by design). At last, after finishing reading the article, I *do* think you understand OOD.. sorry if I wasn't clear about that.

Kristof

Excellent article! by Aslam Khan

Hi Mats,

Really nice article that pulls together a lot of the OO patterns cleanly. This is certainly one of the better coverages of AOP that I have seen. It certainly de-mystifies AOP and makes it attainable for the masses.

Nicely done!

Intercepting Domain Objects by Christian Tzolov

Nice article Mats,

IHMO it is more academic then applicable for real applications. Many java enterprise applications apply technologies like Spring and Hibernate that makes it rather difficult to maintain and test Domain Objects that have been instrumented with proxies or AOP. You might not be able even to plug your AbstractFactory implementation into the persistence layer.

Although the DOs can be instrumented by AOP this looks like breaking their light-weight nature and making the testing dependent of heavy frameworks.

Much content, little sense by Steven Devijver

This article starts by describing how to re-write hibernate and then Spring.

Important challenges for domain-driven design are design-related, not technical.

Persistence is never part of domain-driven design. A domain model solves a specific problem in the problem domain. How state is loaded from and saved to the database is not part of that problem.

In the end, persistence is a sub-problem of how to convert between different domain models in the same system. The domain model used for persistence is hopefully a different one from that used to solve hard domain problems.

Why not make use of context maps?

Erik Evans says: not everything is a large system will be well-designed, so let's try to create a good design for at least the core.

Introducing ORM invariably hurts the design of your domain model and those classes that are affected should best be considered as part of the not-well-designed-part of a system. It's up to each one of us to figure out ways to cope with that.

It would assume however that AOP would seldom be a solution. AOP typically adds behavior where it's not yet available. By modifying your core domain classes with AOP you run the risk of moving from the well-designed area to a less-well-designed area.

Is that what you want? Is persistence so critical that it's allowed to hurt the most fundamental domain classes?

Good and clear ! by Nicolas Mousson

Hi Mats,

Very nice and clear ! I don't think the article is too long, because you explain in details the concepts and problems so clearly, that it's very nice and easy to read.

Just one question : I have tested "PostSharp Laos" AOP solution, and it's very easy to use (especially for people who begins with AOP). Have you already used it ?

Thanks !

Nicolas

Re: Good article by Mats Helander

Hi Kristof,

"Generally I believe good OO design mostly prefers composition in favour of subclassing"

I couldn't agree more! In fact that position is pretty much the underlying premise of the whole article! We refactor from using inheritance where reusable behavior is placed in a common base class to using composition where the behavior is placed in mixins. The subclasses are just the "glue" - their only purpose is to implement the composition! And the only reason we put this composition code in a subclass is so that we can keep the domain class free of infrastructure. We /could/ skip the subclass and put the composition code directly in the domain class, in which case it is really clear that the core message of this article is to use composition over inheritance. Taking that message "all the way", in my opinion, leads to AOP.

Hmmm. I guess you were right I could have explained that in a more compact way than in the article. Seems about seven sentences could have cut it ;-)

Thanks again for the feedback! :-)

/Mats

Re: Excellent article! by Mats Helander

Aslam, Christian and Nicolas - thanks guys! :-)

Christian,

Yes you have a very good point - it ties in a bit to my reply to Steven, but in essence what you address is the next thing I would have turned to in my article if it haden't already been way too long...in fact I wrote a first version of the article covering /only/ that, but it became hard too understand for anyone not already intimately familiar with the subject. Starting to write about the necessary background for understanding that article, I ended up with the current article.

Anyway, without repeating the whole first version of the article in the comments, my opinion is that this is a really interesting topic that will become increasingly important in the future: Which component gets to control object creation and configuration? And furthermore - could components share a set of common infrastructural aspects? Could, say, Hibernate use some standard lazy loading aspect from an aspect library instead of providing its own? I will be returning to this topic in my blog shortly, if you're interested in more of my ramblings on the subject. ;-)

Nicolas,

Nope I haven't tried PostSharp Laos but I will now! :-)

/Mats

Re: Much content, little sense by Greg Helton

Thank you, Steven Devijver. You are absolutely correct. The article and code ignores the value of a layered architecture and dependency inversion. This article is design pattern code smell as described in www.relevancellc.com/2007/5/17/design-patterns-...

The author has made the design pattern into a recipe.

Re: Much content, little sense by Aidan O

Nothing in the article comes close to rewriting any part of Hibernate! Also, it's written in a fairly language/platform agnostic way, so while reading it I assumed I'd be using Spring in my own implementation for the AOP related stuff.

A very well written article IMO.

Java Listings available by Aslam Khan

I put together a really quick set of Java equivalent listings of the C# lists in the article. For AOP, I used SpringFramework 2.5 and AspectJ. You can download the zip from my blog.

Use the sources at your own risk. It compiles cleanly but I have not proofed it for correctness. The intention is just to give a view from Java-land within the perspective of this article. The classes and interfaces for each listing are contained in their own package, viz. infoq.dmm.list1, ..list2, etc.

/Aslam

Re: Java Listings available by Christian Tzolov

Hi Aslam,

The Domain Objects being advised (e.g. Person, Employee) are not Spring beans created though an application context. Instead the instances are created at run-time through the AbstractFactory's. Therefore the tricky part with the above java translation is that it requires a full-blown AspectJ through the Load-time weaving with AspectJ in the Spring .

Although valid this approach violates the simple POJO development style. You are no longer able to write simple JUnit tests because your domain objects depend on Spring, ApsectJ and LTW.

/Chris

Re: Much content, little sense by Mats Helander

Hi Steven,

Thank you so much for your excellent feedback! I will try to address your points as best I can:

"Important challenges for domain-driven design are design-related, not technical."

Completely agreed - as far as it concerns the design of the domain model, all design desicions should be motivated by the shape of the domain, not by infrastructural concerns.

That's just what this article is about - finding ways of moving all the pesky, inevitable infrastructure out of the domain model classes so that they can remain clean and domain focused. This article looks at how to find an architecture that will help deal with the infrastructure - it does not go into the topic of how to design your domain models to make them appropriately represent the domain (more so than noting that it will probably be easier for you to do that activity if the domain model design can be kept free from infrastructural concerns).

"Persistence is never part of domain-driven design."

Agreed - persistence should not influence the design of the domain model.

But my focus is not on persistence. In fact, the article tries to take the opposite perspective.

While today many may only use aspects on their domain model unwittingly if their O/R Mapper uses aspects under the hood to provide features such as lazy loading (the way for example N/Hibernate and NPersist does) the main point of the first version of this article (that I mentioned in my reply to Christian) was that this shouldn't be a necessary state of affairs. Indeed, many of the aspects an O/R Mapper might apply (such as inverse property synchronization) could make sense to use even if the domain model didn't have to be persisted at all (something I also touch more upon in the blog post about DMM, linked above at the end of the article).

So while I agree with you that most of the stuff I describe in my article is probably /currently/ academic (as Christian suggested) to anyone who isn't building an O/R Mapper, I feel that it would be to waste a lot of opportunities to continue to see it this way. I believe I see why you associate this to an article about "building Hibernate" (and to some degree Spring) - and I think it is a very sharp observation, it is just that I would argue that in the long run, seeing this as something that has to do just with persistence would mean passing on the opportunity to exploit the same structures that O/R Mappers often use to deal with this particular type of infrastructural demands.

"Why not make use of context maps?"

Because the article addresses how to manage a domain model within one context - I fully agree with you that if the model required for persistence looks very different from the domain model you want to use for your business logic then splitting them into two separate contexts with one model each and using a context map to keep track and transform between them is a great option. But as I said above this article isn't about persistence - even though it talks about the same kind of aspects that a persistence component would often be the primary (but not necessarily exclusive) consumer of.

But let's say we have a Domain Context and a Persistence Context - each with its own version of the domain model - and a transformer that can transform between the two models. I think what you're saying is that in that case, only the model in the persistence context would need the kind of things I'm talking about in this article. In that case, I would disagree - I think there are many different components, living in many different contexts, that could make use of aspects on their models.

But I do agree that currently we don't see a lot of this outside the world of O/R Mapping...again, if the article had not been so darn long already, this is exactly what I would have liked to go on to discuss, so I can't tell you how happy I am about the critical feedback from you and the others here in the comments section! :-))

"Erik Evans says: not everything is a large system will be well-designed, so let's try to create a good design for at least the core. "

Right, and this article is all about moving the infrastructure out from the core, to keep it clean.

"Introducing ORM invariably hurts the design of your domain model and those classes that are affected should best be considered as part of the not-well-designed-part of a system. It's up to each one of us to figure out ways to cope with that. "

An interesting point when you make it that general and one that I would love to discuss with you (preferrably over beer) :-)

I disagree with you to some extent - well, only on the "invariable hurts" part, really...Nonetheless, I would argue that it isn't what this article is about, as explained above. However, since I've written a few O/R Mappers I'd love to discuss this with you in some other thread! But perhaps you only mentioned it as a segway into:

"It would assume however that AOP would seldom be a solution. AOP typically adds behavior where it's not yet available. By modifying your core domain classes with AOP you run the risk of moving from the well-designed area to a less-well-designed area."

This certainly has very much to do with the article - and here we disagree completely. I think AOP helps move infrastructure (which is what so much of the cross cutting concerns in an application is about) out of the domain model, with two distinct wins:

1) The domain model can be kept clean and domain focused
2) The infrastructure code can easily be written in a modular, generic and reusable way.

"Is that what you want? Is persistence so critical that it's allowed to hurt the most fundamental domain classes?"

On the contrary - I don't think neither persistence nor /any other components/ that might need the types of cross cutting, interception and introduction based infrastructural features (and I would argue that persistence only broke this ice because persistence needed these features the most, first) should be allowed the domain model! This whole article is about attempting to find ways of solving that very issue.

But I think the core of the point you make here is that you're saying that AOP would hurt the design of the target (model) classes...I don't really see why this would be the case? Even with the architecture suggested in this article before an AOP framework is used, while it is of course inconvenient to write the subclasses manually, I don't see how the surrounding infrastructure hurts the domain model, which is kept completely unwitting of the whole thing.

If you could please elaborate on how you see AOP as potentially hurting the design of the target classes I think that would be enlightening to the discussion?

Otherwise, if you agree with me that any such damage is really at worst pretty minimal (such as the need to use AbstractFactory if we're using a runtime subclasser) do you also agree that in that case using AOP doesn't actually conflict with the goals of DDD?

/Mats

Re: Much content, little sense by Mats Helander

Hi Greg,

Thanks for the feedback.

I do agree to some extent with you and others who say that design patterns often represent shortcomings in the programming language. I do mention in the article that some languages, such as Ruby, support features that makes it even easier to implement the core concepts that I'm trying to advocate in this article - reusable infrastructure in the form of mixins and interceptors (favoring composition over inheritance) applicable in any of several different ways including manual subclasses, AOP frameworks and indeed features of some very dynamic languages such as Ruby.

It may be that we agree on the principles this article is actually about - finding ways to make infrastructure code reusable and accessible to many parts of the application without unduely increasing the coupling - but that you react on the admittedly kludgy implementation required in languages such as Java and C#, which does indeed depend on a couple of well known design patterns that are targeted for just these types of languages but aren't really needed in other languages.

I won't argue with you there. But on the other hand, part of the idea behind the article is to show that these concepts are in fact compatible with stongly typed OO - even if the implementation isn't as slick as in Ruby.

However, I'm not sure I follow what you mean by: "The article and code ignores the value of a layered architecture and dependency inversion."

Since I'm not sure what your argument is here I realize this doesn't answer it, but I will note that I'm using the architecture suggested in the article together with a layered architecture and dependency injection, so as far as I know it doesn't ignore nor is it incompatible with with these things...but I'd love if you could elaborate a bit on this.

/Mats

Re: Much content, little sense by Mats Helander

Hi John,

Thanks! :-) Indeed the things I actually try to advocate in the article (reusable DMM infrastructure using mixins and interceptors) are supposed to be platform agnostic, even though the discussion about how to apply these ideas in a strongly typed OO language such as java/C# is of course not really equally agnostic (as noted by Greg).

/Mats

Re: Java Listings available by Mats Helander

Chris,

"You are no longer able to write simple JUnit tests because your domain objects depend on Spring, ApsectJ and LTW."

Assuming that the behavior under test is depending non those aspects being present - which it typically shouldn't be, if we're able to keep our domain model classes free of infrastucture concerns (that are put in aspects). Likewise, the mixins and interceptors that form the aspects should preferably also be easily testable in separation. The test you mention would be the test checking that everything worked together.

/Mats

Re: Java Listings available by Aslam Khan

Hi Chris

Yes, your observation is, to a certain extent, valid. However, the intention was not to get too involved with the SpringFramework but to stick to the theme of the article; i.e. to produce domain classes with minimal infrastructure code, and delegate infrastructure code to other classes.

For me, one of the more important side effects of this article is that you can use simple POJO-based unit tests to test your domain classes and domain rules and logic. Likewise, the infrastructure code is then also easily testable, independent of the domain classes. Just what we want we're aiming for. Like the article suggests, AOP is just a convenience mechanism that does the proxying for you so that you don't have to write all those proxy classes by hand.

Bottom line: make sure that we don't litter our domain classes with infrastructure code from the proxy classes, factory classes, etc, and vis versa.

The kind of testing that you refer to deals more with, what I call, integration testing, i.e. to make sure everything hangs together correctly. In this case, Spring offers convenient ApplicationContext aware JUnit test classes that can be extended for such integration testing.

/Aslam

Typing by Niclas Nilsson

But on the other hand, part of the idea behind the article is to show that these concepts are in fact compatible with stongly typed OO - even if the implementation isn't as slick as in Ruby.


And Mats of course means /statically/ typed, not /strongly/ typed when he talks about differences between Java/C# and Ruby, right Mats? ;-)

Kind regards
Niclas

Re: Typing by Mats Helander

Hi Niclas,

Yes I do, thanks for the correction!

The point is how to bind the domain object to the mgmt infrastructure... by Dan Haywood

... and you are right that AOP is a nice way to extract out this cross-cutting concern. However, the cost of using AOP is either:

  • a more complex build process, or

  • having to manage load-time weaving (problematic at best when coupled with Eclipse OSGi), or

  • if using Spring AOP, then having to register every domain object class as a prototype so that Spring can do its magic (unless 2.5 has some sort of wildcarding, does anyone know?)



I read your article from the viewpoint of how Naked Objects Framework accomplishes this same requirement (my background being that I was part of the team that delivered a 700+ user enterprise system running on Naked Objects). The standard approach for NOF does require a small amount of "obeseness" in the POJOs. Specifically:


import org.nakedobjects.applib.DomainObjectContainer;
public class Customer {
private String firstName;
public String getFirstName() {
getContainer().resolve();
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
getContainer().objectChanged();
}

private DomainObjectContainer container;
public DomainObjectContainer getContainer() {
return container;
}
public void setContainer(DomainObjectContainer container) {
this.container = container;
}
}


The responsibility of the Customer domain object is to (a) ask the DomainObjectContainer to lazily load its properties and (b) to inform the DOC when it has changed. The DOC itself is injected into the DO when it is initially pulled back from the RDBMS. As you probably have realized, the DOC is just an interface that is implemented by the NOF.

My view is that this is a pretty minimal dependency. Of course, you can imagine it's easy enough to write an aspect to completely remove the requirement for the resolve() and objectChanged() calls if desired. Another alternative would be to use CGLib to enhance the DO's bytecode, in the same way that Hibernate does. So long as the DOC gets called, mission accomplished.

As a side note on implementation, the NOF itself wraps each DO in an instance of NakedObject. It is this that manages the lazy and dirty states. Note though that the NakedObject isn't a proxy, rather it is completely generic. The NOF viewers access the metamodel (to paint the generic UI) via this NakedObject wrapper.

Cheers
Dan Haywood

Keeping persistence out of the domain by John Unwin

There is a common view, reinforced by this article, that a domain model should be completely ignorant of persistence. Taken to it's logical extreme it results in the convolutions introduced by Mats in this article.

How about a sensible compromise...

  • A domain object is allowed to know that it is a persistent object

  • A domain object is allowed to control the business logic that related to persistence (e.g. A Customer can refuse to be deleted if there are outstanding Invoices; an Invoice can ensure that all InvoiceLines are deleted before being deleted)


These responsibilities sit comfortably in the domain model. To put these responsibilities elsewhere results in the distribution of business rules among artificial constructs such as "repositories", "Data Access Objects", etc.. The proxies, awkward inheritance hierarchies, etc. that Mats proposes are more such artificial constructs.

So how about this for a solution:

  • Use POJO's

  • Each domain object has save and remove methods



public void remove() {
PersistenceHelper.remove(this);
}


  • Each domain class has methods (static methods in Java terms) for doing such things as findById, getAll, etc.



public static Person findById(final Long id) throws NotFoundException {
return PersistenceHelper.findById(Person.class, id);
}


  • All these persistence methods delegate to a persistence infrastructure (e.g. Hibernate, JPA)

  • Have no direct dependency on a concrete persistence mechanism in the domain objects (no SQL, no JDBC, no imports of Hibernate classes)

  • The actual persistence implementation is supplied or found at runtime (in my case via a PersistenceHelper class)

Dirty Tracking only on Setters? That won't work! by Udi Dahan

First of all, let me commend you on the thorough article. I really enjoyed it.

Now on to the meat...

As we've discussed in the past, handling concurrency requires fetching an object before changing it, just as a reminder, here's my post on the subject: Realistic Concurrency.

In this model, no setters are called on domain objects. Instead, when a method is called, the method changes the fields on the object.

If you were to do dirty tracking by keeping a copy of the object, you'd know that the object was changed. Since you cannot assume that every method changes an object, I don't see how you could use the interception technique to have it handle this scenario.

Interested in hearing your response.

Re: Keeping persistence out of the domain by Dan Haywood

How about a sensible compromise...

  • A domain object is allowed to know that it is a persistent object

  • A domain object is allowed to control the business logic that related to persistence (e.g. A Customer can refuse to be deleted if there are outstanding Invoices; an Invoice can ensure that all InvoiceLines are deleted before being deleted)



I'd agree with those, and this is exactly what Naked Objects does. For example, one can annotate properties and actions with @Disabled(When.UNTIL_PERSISTENT).



So how about this for a solution:

  • Each domain object has save and remove methods



public void remove() {
PersistenceHelper.remove(this);
}


In Naked Objects terms this would be getContainer().disposeInstance(this);



  • Each domain class has methods (static methods in Java terms) for doing such things as findById, getAll, etc.



public static Person findById(final Long id) throws NotFoundException {
return PersistenceHelper.findById(Person.class, id);
}


Well, Naked Objects used to work this way. However, the trouble is that one cannot override or mock such methods. In NOF 3.0 we now use repositories which are registered with the framework and which are instantiated as singletons. These are injected into every domain object that need them, and are accessible (if appropriate) directly from the UI. Putting this functionality as instance methods means that we can then override or provide different implementations (eg running against an in-memory DB rather than a fully fledged RDBMS).

So instead of your proposed solution one would have:

public class PersonRepositoryImpl implements PersonRepository {
public Person findById(final Long id) throws NotFoundException {
return HibernateHelper.findById(Person.class, id);
}





  • All these persistence methods delegate to a persistence infrastructure (e.g. Hibernate, JPA)

  • Have no direct dependency on a concrete persistence mechanism in the domain objects (no SQL, no JDBC, no imports of Hibernate classes)

  • The actual persistence implementation is supplied or found at runtime (in my case via a PersistenceHelper class)


Yup, this is exactly what Naked Objects does. Hibernate is used for the back-end persistence.

Re: Dirty Tracking only on Setters? That won't work! by Mats Helander

Hi Udi,

Thanks, glad you liked it! :-)

>In this model, no setters are called on domain objects. Instead, when a method is called, the method changes the fields on the object.

Yes, this is an issue - you have to code your methods to be stringent in always going by the getters/setters rather than accessing the fields directly, since the field access can't be intercepted. Again, I would love to see real AOP features in the platforms (Java/C#) but since we don't have that, we'll have to use patterns and procedures to achieve a properly modular design of the infrastructure code.

So, to directly answer your question, you'd have to modify the methods so that they updated the objects using the setters (or write them thusly from scratch, not necessarily a bad idea...what if you wanted to extend your class with one that did something kewl in the setter and then the setter wasn't called when the object was updated by your method?).

/Mats

Re: Keeping persistence out of the domain by Mats Helander

Hi John,

Well, there are many ways to skin a cat! :-)

Much of what you talk about can be solved using Inversion of Control, such that you get events when objects are persisted or deleted and can hook into your own methods for performing validation or additional cleanup. On the Active Record approach (Save() methods on the domain objects) I agree with you that it can be a very comfortable API to be able to find the persistence methods on the objects themselves - but I would prefer to mix those methods onto the objects using the approach described in this article! ;-)

But bottom line is I don't disagree with you. If you can get away without cluttering your domain model classes more than in your example, I agree it's certainly no big deal - definitely something that you can live with and indeed nothing that motivates the kind of architecture discussed in this article. I'd wholeheartedly agree with your recommendation to use this approach whenever it was certain that the minimal "bloat" you describe was all we would end up with.

My article attempts to examine a long term strategy for dealing with the particular kind of infrastructural complexity that doesn't have the decency to stop at littering your domain model classes with a few persistence related methods. I hasten to add that I'm not suggesting your applications nor the infrastructure for them wouldn't be complex - I'm saying /if/ you find yourself in a situation where the particular types of infrastructure demands of your application pushes you towards a domain model that is increasingly bloated by infrastructural concerns - and you find that moving the infrastructure ocde out of the domain model entirely would result in less efficient workarounds - then using the approach described in this article can provide a way to refactor back to a clean domain model. Some very complex applications don't run into this issue, for sure, but for those that do I think the article provides sound advice on how to deal with it.

Thanks for the great feedback! :-)

/Mats

Re: Keeping persistence out of the domain by Mats Helander

Hi Dan,

Yes, as long as we don't have good, native support for AOP in the platforms (Java/C# or even JVM/CLR) I agree that each way to write an AOP framework carries its own drawbacks. Personally I think the main drawback with runtime subclassing - relying on the Factory - isn't too bad, since you may well want to have a place to inject a reference to the container/context (just as you mention).

As I said to John, I see no problem with the idea of using AOP to apply interfaces+mixins with persistence methods onto the domain objects. In fact I think it can be a great idea, since it allows all parts of the application to easily access the persistence mechanism - including other aspects.

I think your comment underlines the premise of the article, which is that it can be very convenient to be able to have your infrastructure related features accessible on the objects themselves - including persistence methods! I couldn't agree more with this - it's what the article's all about. :-) In fact, the article looks at what happens when we're so in love with this idea that it starts to seriously threaten the maintainability of our domain model classes...and how we can deal with it.

/Mats

Re: Java Listings available by osman yılmaz

thanks a lot for useful knowledge

www.vitamins5.com

Re: Dirty Tracking only on Setters? That won't work! by Jon B

I have to agree that "static" dirty tracking (such as with an attribute or something) doesn't work in general. For example, what if the setter is implemented like this:



set

{

if (!String.IsNullOrEmpty(value))

{

m_name = value;

}

}



You only want the dirty flag to be set if the m_name is actually changed. Or what happens if you do validation and throw exceptions in the setter. The only way around this that I can see is to do data comparisons to determine the "dirtiness", which has it's own set of (potentially major) issues.

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

Email me replies to any of my messages in this thread

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

Email me replies to any of my messages in this thread

37 Discuss

Educational Content

General Feedback
Bugs
Advertising
Editorial
InfoQ.com and all content copyright © 2006-2014 C4Media Inc. InfoQ.com hosted at Contegix, the best ISP we've ever worked with.
Privacy policy
BT