views:

554

answers:

4

I am currently working on rewriting an application to use Data Mappers that completely abstract the database from the Domain layer. However, I am now wondering which is the better approach to handling relationships between Domain objects:

  1. Call the necessary find() method from the related data mapper directly within the domain object
  2. Write the relationship logic into the native data mapper (which is what the examples tend to do in PoEAA) and then call the native data mapper function within the domain object.

Either it seems to me that in order to preserve the 'Fat Model, Skinny Controller' mantra, the domain objects have to be aware of the data mappers (whether it be their own or that they have access to the other mappers in the system). Additionally it seems that Option 2 unnecessarily complicates the data access layer as it creates table access logic across multiple data mappers instead of confining it to a single data mapper.

So, is it incorrect to make the domain objects aware of the related data mappers and to call data mapper functions directly from the domain objects?

Update: These are the only two solutions that I can envision to handle the issue of relations between domain objects. Any example showing a better method would be welcome.

+5  A: 

Yes. Ask yourself why would a domain object know about such a thing? Not even why, but how? You're going to inject your DAL into your Domain object?

The domain should follow SRP just live everything else. When you traverse your domain you should be unaware if those properties were populated via lazy loading, or hydrated from instantiation.

I've written a domain model that had DAL objects in them and it was a nightmare to maintain. Then I learned NHibernate and my domain consists of POCO's and their respective business logic that I want to encapsulate.

[Edit]

Here's more info. I would only embarrass myself if I tried to explain it. I can only speak of the implementation as a user. Here's a great article on Domain Model management. What you are interested in is the implementation of interceptors, and mixins.

With those tools you can write an employee class as follows:

public class Employee
{
    public Employee()
    {
     Skills = new List<Skill>();
    }

    public string Name { get; set; }
    public IList<Skill> Skills { get; private set; }

    public void AddSkill(Skill skill)
    {
     // Perform Domain specific validation here...

     Skills.Add(skill);
    }
}

As you can see, my data access needs don't impose on my Domain design whatsoever.

Scott Muc
Can you provide an example then of how you handle loading other domain objects within an existing one? For example how you load a collection of Skills for an Employee?
Noah Goodrich
You want repository object to do that...Domain model should be persistance ignorant however there should be another object that does know about persistance that handles that.
Webjedi
Thank you for the update. It looks eerily familiar to Fowler's sample code on Foreign Key Mapping. :-)
Noah Goodrich
You are entirely correct! From the DAL side it's FK Mapping. From the usage side it's called Domain Traversal. Depends on which end of the domain objects you're working from.
Scott Muc
However, how do you get from AddSkill to "SELECT * FROM skills WHERE empID = x"? That's where I'm stuck. And Fowler's simple example places the query logic in the Employee Mapper which seems like not a good idea either...
Noah Goodrich
Actually, the Employee Mapper is responsible for that logic. When the DAL retrieves an Employee object, it is actually returning an Employee Proxy that has the retrieval of the Skills collection baked into it.
Scott Muc
Oh, and if you don't want a proxy object, you and fully hydrate the Skills collection in the DAL by joining Employees with Skills and and mapping from the cartesian product.
Scott Muc
A: 

After having done some further reading and searching for an appropriate pattern, I stumbled upon the Repository Pattern.

From what I can see this is precisely the envisioned solution that allows for Domain Objects like Person to properly delegate queries to the appropriate Data Mapper while leaving the Domain Object and Data Mapper completely abstracted.

Noah Goodrich
+5  A: 

I'm afraid you've slightly misunderstood the intention of the Repository pattern.

The Repository is meant to behave like an in-memory collection of a particular domain object, usually an aggregate root:

interface EmployeeRepository
{
    List<Employee> retrieveBy(Criteria someCriteria);
    void store(Employee someEmployee);
    int countOf(Criteria someCriteria);
    // convenience methods
    Employee retrieveById(String id);
    Employee retrieveBySSN(String ssn);
}

Clients of this code have no idea whether the collection is in memory, like you'd have for unit-testing, or talking to an ORM mapper in some cases, or calling a stored procedure in others, or maintaining a cache for certain domain objects.

This still doesn't answer your question. In fact, you could potentially have domain objects have save() and load() methods that delegate to the right repository. I don't think this is the right way to go because persistence is almost never a part of the business domain, and it gives your domain object more than one reason to change.

Check out this related question for more rambling.

In response to some comments on this answer:

A valid criticism. However, I'm still confused then on how to get a single domain object or a collection of related domain objects when within the context of an existing domain object. – gabriel1836

Let's say an Employee has many Skills. I see nothing wrong with the Employee Repository calling a Skill Repository like this:

// assume EmployeeRepository talks to a DB via sprocs
public Employee retrieveById(String id)
{
    ResultSet employeeResultSet = this.database.callSproc("PROC_GetEmployeeById", 
        new Object[] { id });

    List<Skill> skills = 
        new SkillRepository().retrieveBy(new EqualsCriteria("EmployeeId", id));

    Employee reconstructed = new EmployeeFactory().createNew().
                                  fromResultSet(employeeResultSet).
                                  withSkills(skills).
                                  build();

    return reconstructed;    
}

Another route is instead of calling the Skill Repository, have the Employee Repository call the, in this example, stored procedure to load the result set for skills, then delegate to a Skill Factory to get the list of Skills.

Can't I make a call to the Repository and whether it issues a call to the data mapper or loads the object in-memory is its concern, isn't it? – gabriel1836

Exactly right. I usually mock out the entire data tier in my unit tests this way.

moffdub
A valid criticism. However, I'm still confused then on how to get a single domain object or a collection of related domain objects when within the context of an existing domain object.
Noah Goodrich
Can't I make a call to the Repository and whether it issues a call to the data mapper or loads the object in-memory is its concern, isn't it?
Noah Goodrich
A: 

Hi,

I disagree, I think domain object can access Repositories through Abstract Factory.

public class Client
{
  public void ChangeZipCode(string zipCode)
  {
    // This method access memory or database depending on the factory context
    bool zipCodeExists = RepositoryFactory.Get<IZipCode>().Exists(zipCode);
    this.zipCode = zipCode;
  }
}

By using this pattern, it is not necessary to inject repository interfaces all over your code but only the repository factory.

public abstract class RepositoryFactory
{
  // Class operations
  private static _globalInstance;
  public static CreateGlobalInstance(RepositoryFactory factory)
  {
    _glocalInstance = factory;
  }
  public static I Get<I>()
  {
    return _globalInstance.Get<I>();
  }
  /////////////////////

  ///// this operation should be inherited by:
  ///// * NHibernateRepositoryFactory //
  ///// * Linq2SqlRepositoryFactory ////
  ///// * NUnitRepositoryFactory ///////      
  ///// it depends in your context ////////////////////////
  public abstract I GetRepository<I>();
}

I have been doing it for years without any problems in my unit tests.

Therefore dependency injection is required only in this class RepositoryFactory.

Humberto