The dumbness of a Domain Object

I've been mulling over some ideas for the past couple of days as I work on a project and came to a bit of a head with regards to the dumbness of a Domain Object. There are some base principles that I try to follow with application architecture:

  • The data access layer only creates or accepts domain objects
  • The service layer is responsible for creating or converting data transfer objects to real domain objects
  • Only data transfer objects are sent between the service layer and user interface

So first off, maybe some of these principles are not correct. Hey, principles can always be changed (as long as you reapply them everywhere if they do change and adjust accordingly). I'll leave that to the reader to discuss.

Given these norms, there's a problem (or maybe a question). If the DAL only creates Domain Objects, then it needs to know how to assemble them. After all, it's retrieving data from a source and populating *something*. You could hide the creation in an Assembler or maybe a Factory but still, at some point some data needs to be set as an attribute in a Domain Object meaning that the DAL knows something about the contruction of a Domain Object. The other option is that everything gets assembled in the service layer. If this is the case then why do we even need collections in a Domain Object? They can be dumb and just hold properties that relate to themselves and not worry about things like collections of children.

So here's an example to try to make sense of what I'm talking about. Imagine you have a Media Center tool where users can view album information, list tracks on an album and update the track information. This gives us some simple objects like so (C# pseudo-code):

class Album
{
  int AlbumId;
  string Title;
  string Artist;   
}

class Track
{
  int TrackId;
  string Title;
  int Length;
}

In order to know what Track belongs to what Album, I could have Domain Objects that look like this:

class Album
{
  int AlbumId;
  string Title;
  string Artist;   
  ArrayList Tracks;
}

class Track
{
  int TrackId;
  string Title;
  int Length;
}

Or maybe this:

class Album
{
  int AlbumId;
  string Title;
  string Artist;   
}

class Track
{
  int TrackId;
  int AlbumId;
  string Title;
  int Length;
}

In the case where I have an ArrayList of Tracks in my Album, I don't need the AlbumId to know which Track belongs where. However, if I have to create an Album object out of my DAL, I now know that I have to create Tracks as well. Am I not putting logic of structure of the application in my data layer now? In the last example, I can have some Assembler make calls to the data layer (1 for the album and 1 for the tracks) and put together an object myself. And if the only reason I'm doing this is so that I can show the user at the UI layer an album and it's related tracks, why does my Album Domain Object even need to know about Tracks?

This also brings up the question around using IDs everywhere. Remember we have a principle (right or wrong) that the UI only deals with DTOs and knows nothing about the domain or (gasp) the data access layer. End users don't need to know that "Like a Virgin" has an ID of 4532 in the database. However, if they update the title to a track on the album and want the tool to save that information, it means the UI needs to keep track of all this junk in order to send it back to the system for updating (there's no guarantee that two tracks won't have the same name, even on the same album). So going back to our example where I only want to update one single Track.Title property with a new name the user typed in, do I send back an entire AlbumDTO (with an Array of TrackDTOs). Probably not. I only want to update one track so I only send back one TrackDTO which leads me to the idea that:

  • A TrackDTO must be able to exist on its own (for updating the backend from the UI)
  • It needs to keep track of the Album it belongs to (otherwise how do we know what Album it's for when we update it)

Given this, why should a Domain Object (Album in this case) even bother to have a collection of children (Tracks). So all assembling should be done somewhere other than in the domain objects or data access layer? While this might not be the rule for all Domain Objects, I thought it was something that didn't seem to have a very clear answer (or maybe there is but I'm just missing it).

3 Comments

  • Thanks for the feedback Yves, great stuff. I don't think my blog was completely clear on what I where I was trying to get to (and probably never get there as I could have a 10-hour discussion on this).



    Both book references are one of my staples on the shelf. It's just that when you get to the concrete implementation rather than the theoretical ideas around something, it's when you start questioning things (at least it is for me).



    You do raise some interesting concepts, especially around the performance hits. In the case of a music domain, sure, an album only has 10-20 tracks (with a few exceptions like double and triple albums). However as this was only an example and trying to show a parent-chilld type situation, the architecture might shift if there were hundreds of children to a parent (or mutiple-parents). The lazy load is a good idea but then you get nailed with various trips across the tiers.



    As with any software design, there are so many decision points (with no right answer but many correct ones) and very few hard and fast rules.

  • If there are hundreds of children, changes are that your end user will never be able to weed through such vast amounts of data. It's generally better to offer search functionality and a more "paged" approach. If you're going to use "soap"-based web services, you can use a custom header to pass the page number and the page size. This way you avoid interface polution (e.g. having a regular method and a paged method).

  • I meant "chances" not "changes".

Comments have been disabled for this content.