Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

DDD - How Can I Avoid Crossing Aggregate Boundaries Here?

We're working on a new project (re-writing existing app), and I'm running into problems with my domain model / repository design.

Here is a (simplified) version of two key portions in our domain model:

alt text

As you can see, I have an abstract concept of a Post, which can be things like a Review, a Discussion, a Photo, a Video, etc. Posts can also have comments.

I also have a abstract concept of a Location, which are obviously things like Streets, Cities, Neighbourhoods, etc.

Now, this naturally looked to me as two clear aggregate roots.

So I created two repositories, one called PostRepository, and another called LocationRepository.

This was all working fine, I can add/get any type of Post (or comment), and add/get any type of Location via one of these two repositories.

But now im in the scenario of a "landing page" for a City (for example).

On this page, I need to basically show "all posts for this location".

How is that defined? Well, a Post can be (optionally) tagged at a Location. Implementation detail, so I don't want to go too deep into data (as that's not what DDD is about), but essentially there is geospatial intelligence to work out which posts are contained in a particular location by the shape file of the location, and the latitude/longitude of the tagged Post.

But how can I retrieve this information without crossing the boundaries?

Which repository do I use? Do I need a new one?

If it matters (or for the curious), this is a web application (ASP.NET MVC), with a SQL Server 2008 database and Entity Framework 4.0.

If you need any clarification, let me know.

EDIT

We currently use a modified version of the Specification pattern in order to retrieve domain models.

For example, this is the code in our BLL to retrieve all Review's where Score >= 4:

var reviews = postRepository // GenericRepository<Post>
      .Find() // IQueryable<Post>
      .OfType<Review>() // IQueryable<Review>
      .Where(x => x.Score >= 4)
      .ToList(); // List<Review>

But now I need some code like this:

var reviews = postRepository
    .Find()
    .OfType<Review>()
    .Where( //lat long, or Locations FK )
    .ToList();

The problem is I don't know how to do the above query without adding an intermediary join-entity (LocationPost - as it's a many to many), and add a FK to the Post domain model to that.

But by doing that, I am crossing the aggregate boundaries - aren't I?

like image 591
RPM1984 Avatar asked Nov 26 '10 01:11

RPM1984


2 Answers

Why is this a problem? According to Evans in his book, one AR may very well reference another AR. (You may not however reference a child element in an AR from another AR)

Also, are locations really aggregate roots? The definition of an aggregate root is that it acts as a boundary of concistency. Does that fit the definition of a location? I'd say a location is a value object.

There are pretty much two camps here regarding repositories and AR associations:

One that says that all aggregate roots have to be fetched through their respective repository, and AR's should use soft relations, eg ID's between them

And one that says that aggregate roots may very well fetch other associated aggregate roots and that a repository is merely a way to find aggregate roots.

like image 128
Roger Johansson Avatar answered Sep 30 '22 14:09

Roger Johansson


I would bind post to the location at creation time so that for each location I can get (through a repository) a list of associated posts. It would look like this:

Creation:

var p = new Post(latitude, longitude);
var locations = locationRepository.FindByCoordinates(latitude, longitude);
foreach (var l in locations)
{
    l.AssociatePost(p);
}
session.Save(p);

Retrieval:

var associatedPosts = postRepository.FindByLocation(locationId);
foreach (var p in associatedPosts)
{
    Display(p);
}

Under the hood, the association between posts and location would be implemented as a many-to-many table relationship. There is one problem with this solution: adding a new location requires to scan all the posts and assign them to the new location (if applicable).

Hope that helps.

like image 22
Szymon Pobiega Avatar answered Sep 30 '22 15:09

Szymon Pobiega