Repositories in the Abstract Domain Pattern

August 30, 2006

In this blog entry I will further discuss the repositories as they relate to my previous blog entry A Proposal for an Abstract Domain Pattern.   

1.  The Identity Class 
In the pattern I specified that the abstract domain class defines identity.   So an interesting possibility is to create an actual identity class that inherits from the abstract domain class.    The identity class can throw an exception if you attempt to access any data other than the identity fields.   In C# if you aren’t to picky about the exception that it throws you can just let visual studio generate the class for you and just add the constructor.  Here’s some sample code.

 public class PersonIdentity : APerson
 {
  public PersonIdentity(int personID)
  {
   _personID = personID;
  }
  public override string Name
  {
   get
   {
    throw new Exception(“The method or operation is not implemented.”);
   }
   set
   {
    throw new Exception(“The method or operation is not implemented.”);
   }
  }

  public override APerson Father
  {
   get
   {
    throw new Exception(“The method or operation is not implemented.”);
   }
   set
   {
    throw new Exception(“The method or operation is not implemented.”);
   }
  }

  public override APersonSet Children
  {
   get { throw new Exception(“The method or operation is not implemented.”); }
  }
 }
2.  Making the Domain Class IComparable
For a large repository you can’t work with the entire repository in memory so you need a way to specify part of a repository.  A convenient way to do that is to make the domain class implement the IComparable interface.   Then we can use the concept of a range of values to specify a section of the repository.  See  Caching using a Generic Step Function for an example of how you might define an IRange interface.

We can easily make our APerson class IComparable using the following code.

    public int CompareTo(object obj)
    {
     if (obj == null)
     {
      throw new Exception(“Cannot compare to null”);
     }
     APerson person = obj as APerson;
     if (person == null)
     {
      throw new Exception(“Must be compared to a person”);
     }
     return _personID.CompareTo(person.PersonID);
    } 

2.  Repository Basics

To give some flexibility on how domain objects are identified I’ve decided that the pattern for a repository is that it takes an arbitrary abstract domain object as the lookup key.  In particular since the Identity class inherits from the abstract it can be used to get data from the repository.   Or if you already have a view object perhaps obtained from a pick list you can use the view object directly to get the full object.   A revised example of an abstract domain class APersonSet would be as follows.

public abstract class APersonSet
{
    public abstract APerson Get(APerson person);
    public abstract bool Contains(APerson person);
    public abstract void Add(APerson person);
    public abstract void Remove(APerson person);
    public abstract void Update(APerson person);
    public void Save(APerson person)
    {
         if (Contains(person))
            {Update(person);}
         else
            {Add(person);}
    }
 } 

For the user of the repository the previous code

_person = _repository.GetPerson(_id);

becomes instead

personIdentity = new PersonIdentity(_id);
_person = new _repository.GetPerson(personIdentity);

3.   Retrieving Sets of Objects from a Repository

I think the most general way to access a set of objects in the repository would be something like

public  bool ReadForward(out IList<APerson> list, Range<APerson> range, Criteria<APerson> critera, int maxCount)
public  bool ReadBackward(out IList<APerson> list, Range<APerson> range, Criteria<APerson> critera, int maxCount)

These routines would read an unpredictable number records (but never more than maxCount) and add them to the list.   The unpredictablie record count allows the repository to provide more efficient access through reading multiple database records at a time.  The routines would return true if there was more data to be read and false if not.   The range argument allows us to implement GetNextPage methods and the criteria object allows us to filter the data we retrieve.

These routines would use a default sort sequence based on the identity fields (typically primary key in the database).   We might want to create some other sort sequences based on alternate keys in the database.   This might require some methods like the following.
public class APersonIndex<TIndex> where TIndex : IComparable {…}

public  bool ReadForward(out IList<APerson> list, Range<TIndex> range, Criteria<APerson> critera, int maxCount)
public  bool ReadBackward(out IList<APerson> list, Range<TIndex> range, Criteria<APerson> critera, int maxCount)

4.  Criteria Classes
But what exactly is this Criteria<APerson> class I’ve mentioned above.   At its most basic level a criteria is any test that can be applied to APerson and return true or false depending on whether the APerson object passes the test.  Of course this is just the Specification Pattern in Domain Driven Design.   So we might define an generic interface to indicate this.

public Interface ICriteria<APerson>
{
      bool IsSatisfiedBy(APerson person);
}

If performance didn’t matter that would be all you needed since the repository could sequentially retrieve every person in the database and test each one to see if it satisfied the criteria.   In practice though there will be one or more criteria classes that can be executed with reasonable efficiency.   A simple application might rely only on a few hard coded criteria tests perhaps implemented as stored procedures.  More complicated applications might include a programmable query interface which would require the ability to generate SQL queries within the application itself.

We might have the repository include separate read methods for each PersonCriteria class we define and let the compiler choose the right one to use based on the class.   I’m not really happy with this approach since I don’t see why these different criteria classes belong in the core definition of the domain but I think I’ll leave that subject for a later blog.

A Proposal for an Abstract Domain Pattern

August 24, 2006

In this blog entry I describe a proposed pattern that I will call Abstract Domain.   It’s not an actual pattern yet since I don’t know of any examples of someone using it.    The proposed pattern is related to the idea of programming to interfaces but instead of an interface we use abstract classes.   Here are some of the details.

1.  Create Abstract Domain and Abstract Repository Classes
The abstract domain classes and abstract repository classes form the “abstract core” of the domain.  We can use a naming convention of having an “A” prefix to indicate abstract and the “Set” suffix to indicate a repository.   So ACustomer is the abstract Customer class and ACustomerSet is the abstract customer repository.  

2. An Abstract Domain Class is mostly Identity and Behavior
An abstract domain class has little or no state and as much behavior as possible.   I envision that an abstract domain class might include identifying fields, various abstract properties, and various abstract and virtual methods.  It could define identity by overriding the object.Equals.  

3.  Abstract Repositories are General Purpose Sets
It encompasses the idea of in memory sets as well as database repositories.  One idea I want to explore is the possibility that when a database repository returns a collection of domain objects it might return an in memory repository instead of an array or list.  

4.  Proxies, Views and Snapshots Inherit from the Abstract Domain Class

  •  A proxy is an implementation of the abstract domain class that passes abstract properties and methods on to another abstract domain object for handling.  A proxy may be lazy loaded by having it load the related abstract domain object from a repository only when needed.
  • A view is a kind of lazy loaded proxy except that includes the state information for the fields in the view so it only need to load the full object when a field not in the view is referenced.
  • A snapshot is an implementation of the abstact domain class that throws an exception whenever a property setter or method that updates that the state of the object is executed.

5.  Domain Object State Can Implemented in Various of Ways

For example you could have two versions of a Customer class one which implements state as a set of private fields and one representing states as an internal ADO.Net data row.   The application should work the same regardless of which version you use.

Okay, enough with the theory already.  Lets see some example C# code!   Here’s a simple abstract domain object.

public abstract class APerson
{      
    // we define identity in the abstract class
       protected int _personID;
       public int PersonID {get {return _personID;}}
       public override Equals(object obj)
       {    
              if (obj == null) {return false;}
              APerson person = obj as APerson;
              if (person == null) {return false;}
              return _personID.Equals(person.PersonID);
        }
       // we define abstract properties and methods
       public abstract string Name{get;set;}
       // references to other domain classes use the abstract class
       public abstract APerson Father{get;set;}
       // one to many relationships use the abstract set
       public abstract APersonSet Children{get;}
       // some behavior can be defined in abstract class
       public bool NameIsUnknown()
            { return (Name == null || Name == “”)}
}

Here’s the beginning of an abstract repository.  Its going to need more behavior than this but this blog entry is getting pretty long so that will have to be the subject of another blog entry.

public abstract class APersonSet
{
       public abstract APerson GetByID(int personID);
       public abstract bool Contains(APerson person);
       public abstract void Add(APerson person);
       public abstract void Save(APerson person);
       public abstract APersonSet Find(string criteria);  
}

Here’s a concrete domain class with an example of lazy loading.

public class Person : APerson
{
     // store state as private fields
      private string _name;
      private APerson _father;
      private APersonSet _children;
      // Lets store the repository that the person came from
      // so I can do lazy loading.  
      private APersonSet _repository;
      // for now just a simple constructor
      public Person(int personID, APersonSet repository)
      {
           _personID = personID;  // defined in base class
           _repository = repository;
      }
      // override abstract methods
      public override string Name
           {get{return _name}; set{_name = value;}}
      public override APerson Father
           {get{return _father}; set{_father = value;}}
      // we decide to lazy load the children
      public override APersonSet Children
      {
           get
           {
                 if (_children == null)
                 {
                      _children = _repository.Find(“x where x.Father = this or x.Mother = this”);
                      return _children;
                 }
           }
}

Here’s a PersonView class would be useful for picklists showing just the person name.

public class PersonView : APerson
{
    private string _name;
    private APerson _person;
    private APersonSet _repository;
    public PersonView(int personID, string name, APersonSet repository)
   { 
        _personID = personID;  // defined in base class
        _name = name;
        _repository;
    }
    public override string Name
    {
         get
         {
                if (_person == null) {return _name;}
                else {return _person.Name}
          }
          set
          {
               if (_person == null) {_name = value;}
              else {person.Name = value}
          }
    }
    public override APerson Father
    {
         get
         {
               if (_person == null) {_person = _repository.GetPerson(_id);}
               return _person.Father;
          }
          set
          {
               if(_person == null) {_person = _repository.GetPerson(_id);}
               _person.Father = value;
          }
    }
    // implementation of the Children property is similar to above so I won’t show it here.
}    

Implementing a Generic Step Function

August 21, 2006

In my last blog entry Caching using a Generic Step Function I specified an interface for a step function.  So now I want to move on to describing one neat way to actually implement one.   The trick that I will use is to first do it for a special case which I will call SimpleStepFunction<TKey, TValue> and then show how an arbitrary step function  can be defined using SimpleStepFunction<LowerBound<TKey>,Box<TValue>>.  Here I have defined a generic single value container class Box<T> and make use of the fact that LowerBound<TKey> is IComparable.

The special case SimpleStepFunction<TKey, TValue> has the following restrictions

  1. TKey has a minimum value which is smaller than all other values.
  2. The step function is defined for all values.  We don’t have implement RemoveValue or HasValue.
  3. Values are only set for half open ranges of the form (>= X and < Y).

With these restrictions a step function can be represented simply by a sorted list of key value pairs where the first key is the minimum value of TKey.    If we have already implemented a PointFunction<TKey> as described in the previous blog entry then we can implement SimpleStepFunction as follows.

public class SimpleStepFunction<TKey, TValue>
    where TKey : IComparable
{
    // private fields
    private PointFunction<TKey, TValue> _pointFunction;
    // constructors
    public SimpleStepFunction(TKey minKey, TValue value)
    {
    _pointFunction = new PointFunction<TKey, TValue>();
    _pointFunction.SetValue(minKey, value);
    }
    // methods
    public TValue GetValue(TKey key)
    {
    TKey lastKey = default(TKey);
    UpperBound<TKey> upper = new UpperBound<TKey>(key, false);
    _pointFunction.TryGetLastKey(upper, out lastKey);
    return _pointFunction.GetValue(lastKey);
    }    public void SetValue(Range<TKey> range, TValue value)
    {
    TKey lastKey = default(TKey);
    // if necessary insert a new value at the upper end of range
    if (range.UpperBound.HasValue &&
       !_pointFunction.HasValue(range.UpperBound.Value))
   {
   _pointFunction.TryGetLastKey(range.UpperBound, out lastKey);
   TValue lastValue = _pointFunction.GetValue(lastKey);
   _pointFunction.SetValue(range.UpperBound.Value, lastValue);
   }
   // if necessary insert the value at the lower end of range
    _pointFunction.TryGetLastKey(range.LowerBound.Complement(), out lastKey);
    if (!_pointFunction.GetValue(lastKey).Equals(value))
    {
    _pointFunction.SetValue(range.LowerBound.Value, value);
    }
    // remove any existing values inside the range
    LowerBound<TKey> lower = new LowerBound<TKey>(range.LowerBound.Value, false);
    _pointFunction.RemoveValues(new Range<TKey>(lower, range.UpperBound));
    }
}

Now that I have defined SimpleStepFunction I can show a partial implemention of StepFunction.

public class StepFunction<TKey, TValue>
 where TKey : IComparable
{
 // private fields
 private SimpleStepFunction<LowerBound<TKey>, Box<TValue>>
  _stepFunction;
 // constructor
 public StepFunction() { }
 // methods
 public bool HasValue(TKey key)
 {
  LowerBound<TKey> lower = new LowerBound<TKey>(key, true);
  return _stepFunction.GetValue(lower).HasValue;
 }
 public TValue GetValue(TKey key)
 {
  LowerBound<TKey> lower = new LowerBound<TKey>(key, true);
  return _stepFunction.GetValue(lower).Value;
 }
 public void SetValue(Range<TKey> range, TValue value)
 {
  LowerBound<LowerBound<TKey>> lower =
   new LowerBound<LowerBound<TKey>>(range.LowerBound, true);
  UpperBound<UpperBound<TKey>> upper =
   new UpperBound<UpperBound<TKey>>(range.UpperBound, true);
  Range<LowerBound<TKey>> r = new Range<LowerBound<TKey>>(lower);
  _stepFunction.SetValue(r, new SingleValue<TValue>(value));
 }
 public void RemoveValues(Range<TKey> range)
 {
  LowerBound<LowerBound<TKey>> lower =
   new LowerBound<LowerBound<TKey>>(range.LowerBound, true);
  UpperBound<UpperBound<TKey>> upper =
   new UpperBound<UpperBound<TKey>>(range.UpperBound, true);
  Range<LowerBound<TKey>> r = new Range<LowerBound<TKey>>(lower);
  _stepFunction.SetValue(r, new Box<TValue>());
 }
}

Caching using a Generic Step Function

August 17, 2006

Suppose we have decided to take up the challenge of the Yes option in my last blog entry Simulating Connectivity in a Disconnected World and we need to have a cache to achieve acceptable performance. An ordinary cache which can only cache single values based on a key isn’t enough because we also want to cache the sorted lists.

Suppose for example we execute the query ”Find First 20 Clients where ClientName > ‘C’ order by ClientName and the 20th client we find is “Capital Associates”. Then we’d like to be able add the list of 20 clients that we found to the cache using a cache key of Range(’C’, ‘Capital Associates’)).

So now our cache looks something like a mathematical step function where we have assigned a value to range of keys.  In this case the value we have assigned is a list of key value pairs.

Using C# interfaces lets explore how we might make this vague idea more explicit. Lets start with the idea expressed by Range(C’,’Capital Associates’) first. Ranges are bounded by a lower and upper bound so lets begin there.

First we describe the common behavior of lower and upper bounds in an interface IBound.  Note that I am making these immutable by only providing getters and not setters.

public interface IBound<T>
where T : IComparable
{
    bool HasValue {get;}
    bool IncludesValue {get;}
    T Value {get;}
    bool IsUnbounded {get;}
    bool IsEmpty{get;}
}

Next we describe the specifics of lower and upper bounds.  We can define a ILowerBound as IComparable based on set inclusion by saying that lower bound X is less than lower bound Y if the set of points satisfying X contains the set of points satisfying Y.

  1. Unbounded  is the smallest possible lower bound  
  2. LowerBound(>= X) is smaller than LowerBound(> X)
  3. If X  <  Y then LowerBound(X) < LowerBound(Y)
  4. Empty is the largest possible lower bound

We can define a IRange as a lower bound intersected with an upper bound.  

public interface ILowerBound<T> : IBound<T>, IComparable
where T : IComparable
{
    bool Contains(T value);
    IUpperBound<T> Complement();
    ILowerBound<T> Union(ILowerBound<T> lowerBound);
    ILowerBound<T> Intersect(ILowerBound<T> lowerBound);
    IRange<T> Intersect(IUpperBound<T> upperBound);
}

public interface IUpperBound<T> : Bound<T>, IComparable
where T : IComparable
{
    bool Contains(T value);
    ILowerBound Complement();
    IUpperBound<T> Union(IUpperBound<T> upperBound);
    IUpperBound<T> Intersect(IUpperBound<T> upperBound);
    IRange<T> Intersect(ILowerBound<T> lowerBound);
}

 public interfaces IRange<T>
where T : IComparable
{
    bool Contains(T value);
    ILowerBound<T> LowerBound { get;}
    IUpperBound<T> UpperBound { get;}
    IRange<T> Intersect(IRange<T> range);
    IRange<T> Minus(IRange<T> range);
    bool IsEmpty { get; set;}
    bool IsUnbounded { get; set;}
    bool Contains(IRange<T> range);
}

Next we are going to need a way to represent the results of the query.  I will define a PointFunction to represent the query results and as a tool for implementing the step function.  We will need functionality similar to a SortedList where we sort the list by the key but will also need the added functionality of finding the first key satisfying a lower bound (TryGetFirstKey), the last key satisfying an upper bound (TryGetLastKey), and the ability to remove a range of values (RemoveValues).

public interface IPointFunction<TKey, TValue>
where TKey : IComparable
{
    TValue this[TKey key] { get; set;}
    bool HasValue(TKey key);
    void SetValue(TKey key, TValue value);
    void RemoveValue(TKey key);
    TValue GetValue(TKey key);
    bool TryGetValue(TKey key, out TValue value);
    bool TryGetFirstKey(ILowerBound<TKey> lowerBound, out TKey key);
    bool TryGetLastKey(IUpperBound<TKey> upperBound, out TKey key);
    void RemoveValues(IRange<TKey> range);
}

With the preliminaries out of the way we can finally define an interface for a step function. I can inherit from IPointFunction and just need to add a little functionality associated with setting the value for a range of keys.

public interface IStepFunction : IPointFunction
where TKey : IComparable
{
    TValue this[IRange range] {set;}
    void SetValue(IRange range, TValue value);
    IRange<TKey> GetRange(TKey key);
}

Simulating Connectivity in a Disconnected World

August 4, 2006

Okay here’s the scenario.   You have been giving the task of redesigning an old application mainframe application as a web application.   In this old application there are a lot of selection screens with lists of records where the user could do the following.

  1. Change the sort order of the list.   
  2. Position to a particular record by typing in values in fields at the top of the screen.  The “Position To” fields changed when the sort order changed.
  3. Use page up and page down to navigate through the list.
  4. Pick one or more records from the list.

The old system was optimized for fast response times so there was an index set up for each sort order that the users could select.    No matter how large the list was the user was provided with the illusion that all the records in the list were available at his fingertips.

As the designer the new application of this new web application you have to decide whether you are going to continue to provide the user with the illusion that all the data is at the user’s finger tips.

1.  The “Yes” Option 

You might decide that you don’t want to create a new system that isn’t as just as good as the old system.   In a web application what type of things might you need to do to maintain the illusion that the user has the entire file at his finger tips?

1.  You will need to remember the last and first records in the list so when the user presses page up or page down you can issue an appropriate query against the database.

2. You will need to have a buffer and/or cache to store records in memory so the user doesn’t always have to wait for you to connect and query the database.

3.  In order for the page down button to operate smoothly you may need to pre-fetch the next page of data into the buffer and/or cache in a separate process or thread while the user is looking at the current page. 

2.  The “No” Option 

The “Yes” option sounds like a lot of work so you might decide to tell the user that this new application is not going to work the same as before.   Instead when the user enter value’s in the “Position To” fields the application is only going to return 100 records for the user to page through.    If the record they want isn’t in those 100 records they will have to type different values in the “Position To” fields.   

Your justification for doing this is that it is too much work to maintain the illusion and it’s just not worth the cost.   The users should get used to this new world of stateless disconnected web applications and expect that most things will be better and but some things will be worse than the old system.   You try to console them by adding some filtering capabilities to the list screen.

So which choice would YOU make?

Hierarchical Queries

August 3, 2006

SQL queries against relational databases have the property that the results of executing a query is a relation (e.g. it looks like a table).    This is very nice from the perspective of relational database theory since it means relational operations are closed – they take relations as input and return relations as a result.   But from a practical perspective this has some disadvantages most notably that the data you require may not fit naturally into a single table.   If your data is organized hiearchically then you will either need to run multiple queries or else create queries that have duplicate data.

To take a simple example suppose your have a Customer Relationship Management system containing Customers and Contacts with each customer having multiple contacts.   If a user needs to obtain a set of customers and some of their associated contacts then using SQL we have a couple of alternatives for doing this.

1.   We might have a single query that duplicate the customer information on each contact.   Of course sending all that duplicate customer information over the network is going to hurt performance.

Select Customer.*, Contact.* from Customers Inner Join Contacts on Customers.CustomerID = Contacts.CustomerID  where <condition on customers> and <condition on Contacts>.

2.  We might have two queries duplicating the customer selection criteria in each query.

Select Customer.* from Customers where <condition on customers>

Select Contacts.* from Customers inner join Contacts on Customers.CustomerID = Contacts.CustomerID where <condition on customers>  and <condition on contacts>.

This second query could alternatively be written using nested queries as follows

Select Contact.* from (Select Distinct CustomerID From Customers Where <Condition On Customers>) As T1 Inner Join Contacts On T1.CustomerID = Contacts.CustomerID where <Condition On Customers>.

Various alternatives have been tried to get around this practically difficulty.   For example in ADO Microsoft created a difficult to use hierarchical query language and associated hierarchical record sets.   I may not have this exactly correct but the syntax in this example would be something like the following.

SHAPE {SELECT * From Customers Where <condition on customers>} APPEND  ({SELECT * From Contacts where <condition of contacts>} RELATE Customers.CustomerID To Contact.ContactID

Now that Microsoft has moved on to ADO.NET and data sets this I imagine this hierarchical query language will disappear from use as old ADO application are replaced.

In an end user query tool I was recently working on I tried a different approach to the problem.    Instead of having a single query I had a “query tree” where the query was organized into a hierarchy of simpler queries.   This was a visual tool so there wasn’t an actual query language but it worked as if you added a new “hierarchical join” construct to the language.  So you might express the above query as

(Select * from Customers Where <condition on customers>) Hierarchical Join (Select * from Contacts where <condition on contacts>) On Customers.CustomerID = Contacts.CustomerID

The following is a list of the various rules for defining a query tree that I used in the End User Query tool without trying to turn it into a language syntax.   Note that in the query tool all joins were predefined and could not be constructed on the fly.   The query tool returned results either in a database as separate tables for each subquery or in an Excel workbook with a separate worksheet for each subquery.

1.   A query tree is a set of flat queries organized into a hierarchy.   A table cannot appear twice in the same subquery but the same table can appear in different subqueries.

2.   The top query in the hierarchical query can optionally be empty in which case the subqueries are treated as independent queries.    This is useful if you want to group a batch of unrelated queries into a single request.

3.   A subquery can optionally be specified to have no output.   In that case it would still affect the records returned by child or parent queries.

4.   A top level query consists of a single initial table followed by a sequence of inner or outer joins.

5.   A subquery consists of a single initial hierarchical join to a table in its parent query followed by a sequence of inner or outer joins.   

6.   In addition to creating subqueries the user can insert summary queries into the query tree.   Summary queries have no tables in them and can only have a single child query. 

7.   Cross product joins are not allowed in a subquery.   So if you include a 1-N link in a query you can only add another 1-N link if it is linked to the N side of the previous 1-N link.

8.   Each subquery has a list of fields, a selection criteria and sort order.   You can specify that some fields are hidden in which case it can be used in selection critera or computations but won’t appear in the output.

9.   A field in a subquery can either be a constant expression, a field from a table in the subquery, a summary function applied to a field in one of its child queries, or a computed field calculated from other fields in the subquery.

Now that we’ve defined a query tree we need to define how to interpet it and turn into a regular SQL expressions.    The special cases can get tricky (for example how do handle nested summary queries) but some of the basic cases are described below.

 1.   For a regular child query you determine the key fields needed in the join expression for between the child and its parent query and create sql something like

Select <child query fields> from (Select distinct <key fields> from parent query SQL) as T1 on <join expression with parent>  <child query tables and join expressions> where <child query selection criteria>.

2.    In a parent query a summary field based on a child query field would create SQL something like the following.

Select Sum(Select childField from <child query join and select SQL>) As SumOfChildField, etc.

3.    If the selection criteria for a parent query involves summary fields of child queries this is handled by correlated subqueries something like the following.

Select <parent query fields> from parent tables where Sum(Select childfield from <parent table joined with child query> inner join <child table>  on <parent child join expression>  inner join <other child tables>) > 1000.

4.   Summary queries added to a hiearchy would be interpreted as

Select <summary fields> from <child query join sql> where <child query selection criteria> having <parent query select query>

What is a View in Domain Driven Design?

August 3, 2006

In computer science we use the term “View” in a variety of ways

1. Relational Database Views
In relational databases a view is a virtual table defined by a query against other tables. So “view” in this sense refers to a way of looking at a subset of a large database of information. Frequently views are defined to be specific to some task – showing only the information needed to perform that task.

2. Model-View-Controller
Model View Controller is an object oriented design pattern first popularized by Smalltalk. So a “view” in this sense refers to what the user sees when when working with a computer application built on the model. In a sense it is a the users API (application programming interface). You can think of the controller as being responsible for translating actions in the users API into actions in the models API.

In a recent blog Sergio Bossa was trying to work out what a view might be in the context of Domain Driven Design (see http://sbtourist.blogspot.com/2006/07/constructing-view-objects-with-builder.html).  His definition of a view is “View objects simply represent data requested by a view and extracted from one or more domain objects. ” The general problem he is dealing with is that for specific tasks (like choosing an object from a list) the user doesn’t need the full capabilities of a domain object and creating those domain objects can create an unacceptable hit on performance. Here he is using the term view close to the relational database notion of a view being a subset of the data.

It seems a little odd to use a relational concept in a object oriented context so perhaps we should expand on this notion of view and add some behavior. We might instead try a definition like “A view represents a set of the data and behavior which is useful for a specific set of tasks”. This definition makes a view sound more like a sub-domain of some sort. Since the task of choosing an object from a list is such a common one this might be treated as a generic sub-domain. And in fact I suspect this is how most large projects handle picklists – to avoid repetition they try to come up with (or purchase) a generic solution that can be shared.

In my comment on Sergio’s blog entry I was focusing on what is probably the most common case where for performance reason developers feel the need to bypass domain objects and create a view – namely picklists. Here the user has the task of choosing an object of interest and only needs the data and behavior relevant to this task. Typically this involves listing a small number of data fields for a collection of domain objects and providing the functionality for paging, filtering, positioning, sorting and selecting from this list.

In my comment I suggested that perhaps the set of data fields needed by the user to recognize a domain object should be made part of the domain as an interface and that the repository could take this interface object as the lookup parameter when getting a domain object. Here I was getting away from the general concept of “view” and instead trying to focus on the question of how users recognize real objects from data about them. Is that a domain concept?

It might be helpful to ask the question “A view of what?” As used here a view object is not a view of a domain object but rather a view of the underlying real or conceptual object that the domain object represents. So in a sense both the view object and the domain object are views of an underlying conceptual object. Also different domains in an enterprise application may contain different views of the same conceptual object. In some sense the entire domain is a “view” of the real world entities an enterprise has to deal with.

Putting these musings together with some of the concepts in Chapter 15: Distillation of the Domain Driven Design book I would summarize all this with the following recommendations for handling views.

 1.  Create an ABSTRACT CORE containing interfaces for the data elements of domain objects you want to put into the views.    The interfaces in the abstract core represent a minimal set of data elements that the users expect to see in lists.   Domain objects would inherit from this abstract core.

2.   Create a GENERIC SUBDOMAIN to handle pick lists.   Pick lists would be populated with interface members from the abstract core.   Include functions such as sorting, filtering, positioning, paging, and picking in this generic subdomain.

3.   If you need more complex and general purpose views for reporting purposes this would be a different GENERIC SUBDOMAIN.

4.   If you have specific tasks where using the standard domain objects causes too great a performance hit try creating a task specific SEGREGATED CORE for those tasks.   For efficiency reasons you may occasionally need specialized versions of the domain objects for the segregated core but they would always inherit from central objects in the abstract core.  

Hello world!

August 3, 2006

Welcome to WordPress.com. This is your first post. Edit or delete it and start blogging!


Follow

Get every new post delivered to your Inbox.