posted on Friday, October 01, 2004 12:31 PM by mlorengo

Domain Modeling with The Virtual Cellar and Repositories

I did some initial modeling on the business domain for my Virtual Cellar application. I was mainly focusing on two major classes. Producers of Wines (Wineries) and Wines. Here's my initial set of requirements that I need to model.

For the Producer
  1. A Producer has a Name
  2. A Producer has Contact Information consisting of an Address (including City, StateOrProvince, PostalCode), Telephone Number, Fax Number, Email Address and Website
  3. A Producer must produce anywhere from 1 to many distinct wines over the Producer's existence
  4. A Producer can produce anywhere from 0 to many distinct wines per year
  5. A Producer can produce Wine made from 1 or More Varieties of Grape
For the Wine
  1. A Wine has a Name
  2. A Wine has a Vintage
  3. A Wine can have only 1 Region
  4. A Wine may have a Description
  5. A Wine is made from one or more grape Varieties
  6. A Wine may be designated as a Reserve, Estate Bottled, Estate Grown etc...

By looking at these initial sets of attributes, I can think of  some other possible classes that I need.

Region
  1. A Region has a name
  2. A Region can contain one or more other Regions
  3. A Region may have a particular classification (DOC, DOCG, DO, DAO etc)
  4. A Region may have a Description
Varietal
  1. A Varietal has a name
  2. A Varietal may have a description

Based on the above initial set of rules, I've come up with the following class diagram.

I'm unsure about a couple things on this picture, (I also got lazy and neglected to make the private references available via public properties, just assume that they are there for now).

  1. Should I break out the Address information into a separate class? Some Producers may have multiple locations, such as a Tasting Room, or other Corporate Locations. I don't think I'll do that for now, version 1 will only support a primary location.
  2. I don't have any methods defined for these classes. I haven't described some of the operations that I'll want to do with these classes, but at a minimum I'll want to

    Do CRUD operations on Wines available for a Producer
    Do CRUD operations on Producers
    Do CRUD operations on Regions and Varietals

How do I go about doing this? Well, I laid out my applications architecture in this post, So I see having an overall Manager class that provides TableDataGateway objects which perform CRUD operations using coarse grained (mainly classes in the business domain) interfaces. However, I've been reading a couple of interesting blog entries discussing different implementations of the Repository pattern.

Steve Maine presented a VS2005 Solution demonstrating his implementation. I took that and created a class diagram and sequence diagram so as to better illustrate his proposal. Please read his most excellent post before continuing.

His solution uses the generics enhancement in C# 2.0. This helps clean up the coding around the RepositoryFactory which is responsible for getting a Class Factory of the appropriate type. I have no problem with this.

The other interesting thing that he does is use Nested Classes to allow the Repository Access to private Customer Key field. This means that you essentially tie all these classes together in one class which can be a huge unwieldy mess. To get around having on big .cs file he uses C# 2.0 Partial classes. Instead of using Nested Clasess and C# 2.0 features, I think a better solution would be to use the internal modifier to make the field accessible to classes in the same assembly.

After looking further at the code I did a quick Sequence Diagram, please let me know if you find any errors (Right Click & Open in a New Window)...

There is only one thing that bothers me about this, and it may just be an oversight (of course I could have misread it as well). It appears that a new <Customer>Repository is getting created each time the GetRepository<>() method is being called. Here's the code from his post...

public IRepository<T> GetRepository<T>()
{
	IRepository<T> repository = null;

	if( typeof( T ) == typeof( Customer ) )
	{
		repository = (IRepository<T>) new Customer.CustomerRepository( this );
	}
	else if( typeof( T ) == typeof( Address ) )
	{
	repository = (IRepository<T>) new Address.AddressRespository( this );
	}

	return repository;
}

A better solution may be to cache the newly created instance in the RespositoryFactory class so that it can be reused, rather than creating new ones.

I like Jimmy Nilsson's approach as well. This corresponds more to the TableDataGateway pattern I've been thinking about but adds a more cohesive (to me) way of thinking about it. Most likely I'll use a synthesis of multiple models for my implementation.

More on that later.

Update: Added link for Jimmy Nilsson's post

Comments