ActiveRecord Example and Object-Relational Mapping Anti-Pattern - Not Always So Cut and Dry

Ayende makes a good point about my previous Castle Project's ActiveRecord Example where I allowed foreign keys relationships to seep into my domain model as shown below. Ideally the Post object would know nothing about foreign keys like BlogId and CategoryId shown below:

 

/// 
/// Summary description for Post
/// 
[ActiveRecord("Posts")]
public class Article : ActiveRecordBase<Post>
{
    private int _id;

    [PrimaryKey(PrimaryKeyType.Native, "PostId")]
    public int Id
    {
        get
        {
            return _id;
        }
        set
        {
            _id = value;
        }
    }

    private int _blogId;

    [Property]
    public int BlogId
    {
        get
        {
            return _blogId;
        }
        set
        {
            _blogId = value;
        }
    }

    private int _categoryId;

    [Property]
    public int CategoryId
    {
        get
        {
            return _categoryId;
        }
        set
        {
            _categoryId = value;
        }
    }

    private string _title = string.Empty;

    [Property]
    public string Title
    {
        get
        {
            return _title;
        }
        set
        {
            _title = value;
        }
    }

    private string _description = string.Empty;

    [Property]
    public string Description
    {
        get
        {
            return _description;
        }
        set
        {
            _description = value;
        }
    }
}

 

The Object Relational Mapper would ideally take care of the mapping in the background. BlogId and CategoryId would ideally be non-existent in the class and the respective Blog and Category objects would be there instead.

 

private Blog _blog;

public Blog Blog
{
    get
    {
        return _blog;
    }
    set
    {
        _blog = value;
    }
}

private Category _category;

public Category Category
{
    get
    {
        return _category;
    }
    set
    {
        _category = value;
    }
}

 

My main reason for writing the example as such was for clarity and the fact that I wasn't planning to talk about relationships. I hadn't even created Blog and Category Domain Objects :)

As I started thinking about the purity of the domain model, however, things rarely are so cut and dry. You would have to check with your O/R Mapper, because some, like DLinq, still require at least an FK private member, like _blogId and _categoryId, to be present within the Post Class. You don't have to use them or expose them as a property, but as of the May 2006 CTP of DLinq, you cannot remove the foreign keys from the class as they are required for persistence.

Of course, one really has to determine what is pure and not-so-pure when you have something like EntityRef required of DLinq and similar objects used by a number of O/R Mappers to support lazy-loading:

 

private EntityRef<Blog> _blog;

[Association(Storage="_blog", ThisKey="BlogId")]
public Blog Blog
{
    get { return _blog.Entity; }
    set { _blog.Entity = value ; }
}

 

Now we have introduced classes that are particular to DLinq and hence this domain model is really not self-contained and clear of persistence-related items. Remove DLinq from the picture and this domain model won't compile or work.

In many cases, your domain model won't be pure. It will have O/R Mapping scraps hanging around to support the object-relational impedance mismatch. There may be attributes; special interfaces implemented to best help with business object tracking, performance and synchronization; proxies to help with lazy loading, and anything else to help in the real-world :)

Realistically I don't think things are going to be so pure. Certainly I think there are best practices to maintain looser-coupling and to keep the domain layer self-contained and portable, but be wary of trying to get things perfect. If your relational-model starts to creep in to your domain model because your O/R Mapper requires it or because it makes sense in this imperfect world, do it. Try to encapsulate it as much as possible for later change and make a note to yourself that things could be better. Things could always be better :)

 

Source: David Hayden ( .NET Developer )

Filed: Design Patterns, O/R Mappers, Linq for SQL Tutorials

 

posted on Wednesday, June 14, 2006 2:03 PM

Main

News

Green Tea

.NET Development

Enterprise Library

Patterns & Practices