How to properly map entities to domain models in n-tier architecture?

I created a mid-size project following Project Silk's structure. However, I have trouble mapping the entities I retrieve from my repositories into domain model objects for use in the Web project. I have posted a similar question here with all the code and haven't received the help I'm looking for.

My application's architecture follows Project Silk's very closely. The data tier holds the repositories and model POCOs. The Business Logic layer holds the services. Inside these services, we map objects from the Data Tier to the Model objects in the business layer.

internal static Model.User ToDataModelUser(User userToConvert)
{
    if (userToConvert == null)
    {
        return null;
    }

    Model.User modelUser = new Model.User()
    {
        UserId = userToConvert.UserId,
        AuthorizationId = userToConvert.AuthorizationId,
        DisplayName = userToConvert.DisplayName,
        Country = userToConvert.Country,
        PostalCode = userToConvert.PostalCode,
        HasRegistered = userToConvert.HasRegistered,
    };
    return modelUser;
}

internal static User ToServiceUser(Model.User dataUser)
{
    if (dataUser == null)
    {
        return null;
    }

    User user = new User()
    {
        UserId = dataUser.UserId,
        AuthorizationId = dataUser.AuthorizationId,
        DisplayName = dataUser.DisplayName,
        Country = dataUser.Country,
        PostalCode = dataUser.PostalCode,
        HasRegistered = dataUser.HasRegistered,
    };
    return user;
}

My question is how do I map objects like this when they have many-to-many relationships? For example, lets say a User has an ICollection Roles. That means my Role has an ICollection Users. When I'm mapping a users via ToDataModelUser or ToServiceUser, I now have a Roles property to populate. Thus the code from above will look like this:

internal static Model.User ToDataModelUser(User userToConvert)
{
    if (userToConvert == null)
    {
        return null;
    }

    Model.User modelUser = new Model.User()
    {
        UserId = userToConvert.UserId,
        AuthorizationId = userToConvert.AuthorizationId,
        DisplayName = userToConvert.DisplayName,
        Country = userToConvert.Country,
        PostalCode = userToConvert.PostalCode,
        HasRegistered = userToConvert.HasRegistered,
        Roles = new Collection<Role>()
    };

    foreach (Role role in userToConvert.Roles)
        modelUser.Roles.Add(RoleServies.ToDataModelRole(role));

    return modelUser;
}

Now here comes the problem, if you look at RoleServices.ToDataModelRole(Role role) this is what you get:

internal static Model.Role ToDataModelRole(Role roleToConvert)
{
    if (roleToConvert == null) return null;

    Model.Role role = new Model.Role()
    {
        Description = roleToConvert.Description,
        RoleId = roleToConvert.RoleId,
        RoleName = roleToConvert.RoleName,
        Users = new Collection<User>()
    };

    foreach (User user in roleToConvert.Users)
        roleToConvert.Users.Add(UserServices.ToDataModelUser(user));

    return role;
}

As you can easily see, if you run this you will get a stack overflow error b/c we will be going from User >> Role >> User >> Role >> etc.. when trying to do the mapping. If I don't map the navigation properties, I don't have access to them in the web project. I have a feeling I am totally missing something here.

Answers


You could create an overload for the ToDataModelX methods. Pass a Boolean to de/activate the loading of the subordinate object. Instead of always loading the a Role's Users, only load them when directed to do so.

internal static Model.User ToDataModelUser(User userToConvert)
{
    return ToDataModelUser(userToConvert, true);
}

internal static Model.User ToDataModelUser(User userToConvert, Boolean loadRoles)
{
    if (userToConvert == null)
    {
        return null;
    }

    Model.User modelUser = new Model.User()
    {
        ....
        Roles = new Collection<Role>()
    };

    if (loadRoles)
    {    
        foreach (Role role in userToConvert.Roles)
            modelUser.Roles.Add(RoleServies.ToDataModelRole(role, false));
    }

    return modelUser;
}


internal static Model.Role ToDataModelRole(Role roleToConvert)
{
    return ToDataModelRole(roleToConvert, true);
}

internal static Model.Role ToDataModelRole(Role roleToConvert, Boolean loadUsers)
{
    if (roleToConvert == null) return null;

    Model.Role role = new Model.Role()
    {
        ....
        Users = new Collection<User>()
    };

    if (loadUsers) 
    {
        foreach (User user in roleToConvert.Users)
            roleToConvert.Users.Add(UserServices.ToDataModelUser(user, false));
    }

    return role;
}

Need Your Help

Gem update on Windows - is it broken?

windows ruby rubygems

This is a follow-up to this question.

About UNIX Resources Network

Original, collect and organize Developers related documents, information and materials, contains jQuery, Html, CSS, MySQL, .NET, ASP.NET, SQL, objective-c, iPhone, Ruby on Rails, C, SQL Server, Ruby, Arrays, Regex, ASP.NET MVC, WPF, XML, Ajax, DataBase, and so on.