1

I'm working on an ASP.NET MVC site, using EF4 and Automapper. I'm making good progress, and feel fairly good for the design (considering this is my first stab at a true DDD architecture). But I'm starting to see some things that I'm not too comfortable with.

The questions I have probably relate more to my lack of experience with Automapper and DDD, so they may be fairly easy for some of you to answer and point me in the right direction. There's also a very good chance I'm going about this completely wrong, but my direction is based on what I'm reading on here, some from searching, and some from asking my own questions. But I'm also starting to realize that some of the "advice" I'm seeing is contradictory to each other.

Quick background, my domain model is simply the generated POCOs from EF4 POCO template (anemic). These are used in my service layer, and they are also exposed to my application via the service layer. I don't have any sort of DTO, just my anemic domain model, view models, and Automapper to map the two, per the advice I was given. This is all fine and dandy assuming they can be mapped one-to-one.

My problems come in when I have to flatten my domain models into one view model, or when I need some business logic to define a view model property.

Two examples:

Flattening

I have two models, User and UserProfile. This is a one-to-one mapping, so it already is logically flat -- it's just in two separate models. My view model needs properties of both User and UserProfile in one object. From what I've seen, Automapper has no easy way to do this, so what I did was extend my User POCO, adding the needed UserProfile properties to it:

public string UserName
{
    get { return UserProfile.UserName; }
}

I'm not a big fan of this as it seems to violate DRY, and may become a bit of a pain the more I have to do this.

Encapsulating business logic

I have another case where a User property isn't stored, but rather derived, and needs to do some logic before returning a value. For example, a profile image URL will depend on whether they registered with Facebook or Twitter. So I have something like this:

public string ProfileImageUrl
{
    get
    {
        if (User.TwitterId != null)
        {
            // return Twitter profile image URL
        }
        if (User.FacebookId != null)
        {
            // return Facebook profile image URL
        }
    }
}

I'm pretty sure this isn't something that Automapper is responsible for, but I'm not sure if this should be done with a domain model extension if I want to keep it anemic. So I'm not sure where this belongs.

I'm not completely stuck on having my domain model be anemic (I know that's its own debate), but I do anticipate this domain model being used by multiple applications with different view models and validation.

Thank in advance.

UPDATE: In response to @jfar, my main issue with the flattening example is that it seems like this should be something Automapper should be able to do. If not, then I can live with this option. I was just looking for someone to double check my design to make sure there wasn't a better way to do this.

My issue with the encapsulating business logic example, is that I'm violating the anemia of my domain model. In some cases, this may even need to hit the repositories to pull certain data for business logic, and this seems like a bad design to me.

The other thing I'm starting to wonder, is if I shouldn't have a DTO layer, but I honestly don't want to go that route either (and per the question I linked above, it seems to be more accepted not to use DTOs w/ DDD).

Community
  • 1
  • 1
Jerad Rose
  • 15,235
  • 18
  • 82
  • 153

4 Answers4

1
  1. Flattening: You can chain your mappings together using Automapper's ability to map to an existing object. Chain Example
  2. Encapsulatng Business Logic: I don't really see your problem. However, if you want Automapper to be responsible, take a look at before and after maps or custom resolvers. Custom Resolver Example
Firestrand
  • 1,305
  • 10
  • 19
  • Thanks, your chaining link is what I was looking for regarding flattening. And I believe you and Brian were right regarding allowing these properties to exist on my model. That is the direction I ultimately ended up going. – Jerad Rose Apr 23 '11 at 14:03
1

Why are you so intent on keeping your domain model anemic? It looks to me like your domain objects are nothing more than DTOs.

The ProfileImageUrl belongs on your domain class that has the necessary information to return the correct URL. I'd imagine this is UserProfile. If a single class doesn't have the needed information thats what a service is for. You either create a method on your service that returns the URL or a composite object like Darin Dimitrov suggested.

Firestrand and Darin Dimitrov's answers are both good ways to accomplish flattening.

Brian Cauthon
  • 5,524
  • 2
  • 24
  • 26
  • Thanks Brian, you are right. I ended up seeing that this belongs on my domain model, and that seems to be the cleanest approach. – Jerad Rose Apr 23 '11 at 14:08
0

Define a model that regroups those two models:

public class UserWithProfile
{
    public User User { get; set; }
    public UserProfile Profile { get; set; }
}

then have your service layer return this model. Another possibility if there is a 1-to-1 mapping between User and UserProfile is to define the following model:

public class UserWithProfile: User
{
    public UserProfile Profile { get; set; }
}

Then define a view model containing only what is needed by the given view:

public class SomeUserViewModel
{
    ... only properties needed by the given view
}

next define a mapping between your model and your view model:

Mapper.CreateMap<UserWithProfile, SomeUserViewModel>()
      ...

and finally in your controller use the service layer to fetch the model, map it to a view model and pass this view model to the view for rendering, pretty standard stuff.

Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • Thanks a lot for your response. Question though, does SomeUserViewModel have another property of type UserProfile named Profile? If so, this is not flattening my domain model into a flat view model. If not, how does Automapper flatten this with the example you've given? Thanks again. – Jerad Rose Apr 04 '11 at 23:14
  • @Nitish, no, it only has simple properties needed by the view such as FirstName, LastName, Email, ... – Darin Dimitrov Apr 05 '11 at 05:52
  • Ok, so I guess I'm back to my original question: how can I flatten a nested domain model to a flat view model with Automapper? – Jerad Rose Apr 05 '11 at 07:21
  • @Nitish, the documentation has many examples: http://automapper.codeplex.com/wikipage?title=Flattening&referringTitle=Home – Darin Dimitrov Apr 05 '11 at 07:43
  • Looking at there examples, it looks like their flattening depends on you have properties prefixed with "Get" on the parent, and that it's mainly for aggregating. It looks like @Firestrand's example of chaining is what I need. Thanks again for your help, Darin. – Jerad Rose Apr 23 '11 at 14:05
0

I searched and read about flattening more, and the more I searched, to more it looked like Automapper should be handling this automagically. After playing around a bit, I found a way to get this to work, though I still can't find any examples of doing this, even with Automapper's documentation.

The key is to name the property on the destination object with the fully qualified name of the source object graph.

Source objects:

class User
{
    int Id { get; set; }
    FacebookUser FacebookUser { get; set; }
}

class FacebookUser
{
    string UserName { get; set; }
}

Destination objects:

class UserViewModel
{
    int Id { get; set; }
    string FacebookUserUserName { get; set; }
}

So:

UserViewModel.Id -> User.Id
UserViewModel.FacebookUserUserName -> User.FacebookUser.UserName

Maybe this should have been obvious to me, and maybe it is for most people. And now that I think about it, it makes sense -- it's really the only way it could work. I just didn't figure it out until now.

Jerad Rose
  • 15,235
  • 18
  • 82
  • 153
  • It is covered in the case you mentioned above, although they don't go into much detail when you go 'n' + 2 levels deep : http://automapper.codeplex.com/wikipage?title=Flattening (Edit: Jarad Rose references the documentation below as well) – Timbob Aug 19 '11 at 18:13