Aug 10 2009

NerdDinner with Fluent NHibernate Part 1 – The domain model

Category: Uncategorizedbengtbe @ 06:00

This is the first post in a series of three where I’m going to see how we can change the NerdDinner project to use Fluent NHibernate instead of LINQ to SQL:

Introduction

NerdDinner.com is a site where you can organize dinner meetings for nerds, but even more, it is a great tutorial to the ASP.NET MVC framework. If you want to learn this framework (you should!) download the free tutorial (186 pages) from the site and get going!

NerdDinner uses LINQ to SQL as a data access layer generator. LINQ to SQL is perfectly suitable for this project, since the domain is small, and not very complex. However in these posts I would like to explore how the project would look using a real Object/Relational Mapper (O/RM). More specifically, I’m going to use Fluent NHibernate.

Preperations

First I downloaded the latest source code from CodePlex using the Subversion client TortoiseSVN. Then I added references to the NHibernate, Fluent NHibernate, and LINQ to NHibernate assemblies.

The existing domain model

Most of the domain model in NerdDinner is auto generated from the database as partial classes by the LINQ to SQL designer. You can find the Dinner and RSVP class in the NerdDinner.Designer.cs file; however since they are auto generated they are not very readable.

The Dinner class is also extended (using partial classes) with additional methods in the Dinner.cs file.

The new domain model

Since most of the existing domain model is auto generated we need to write a new domain model. The new Dinner class looks as follows:

public class Dinner

{

    public Dinner()

    {

        RSVPs = new List<RSVP>();

    }

    public virtual int DinnerID { get; private set; }

    public virtual string Title { get; set; }

    public virtual string Description { get; set; }

    public virtual DateTime EventDate { get; set; }

    public virtual double Latitude { get; set; }

    public virtual double Longitude { get; set; }

    public virtual string Country { get; set; }

    public virtual string Address { get; set; }

    public virtual string HostedBy { get; set; }

    public virtual string ContactPhone { get; set; }

    public virtual IList<RSVP> RSVPs { get; private set;}

    public virtual void AddRSVP(string attendeeName)

    {

        if (dinner.IsUserRegistered(User.Identity.Name)) return;

        RSVPs.Add(new RSVP

                      {

                          AttendeeName = attendeeName,

                          Dinner = this

                      });

    }

    public virtual bool IsHostedBy(string userName) { … }

    public virtual bool IsUserRegistered(string userName) { … }
    public virtual bool IsValid { … }

    public virtual IEnumerable<RuleViolation> GetRuleViolations() { … }

}

The new RSVP class looks as follows:

public class RSVP

{

    public virtual int RsvpID { get; private set; }

    public virtual Dinner Dinner { get; set; }

    public virtual string AttendeeName { get; set; }

}

If you wonder why every method is marked by virtual, this is a requirement of NHibernate in order to support Lazy-Loading. I explained this in more detail in the post Mapping a Twitter like domain with Fluent NHibernate.

The public interface of the domain model is more or less identical to the existing domain model. However I did make some changes.

Protect thy privates!

One of the changes I made was to make some of the property setters private; DinnerID, RSVPs, and RsvpID. Since the consumer code (the controllers) is not supposed to change to these properties we want to communicate this to our fellow programmers. The next change is also about protecting privates.

New method to add a RSVP to a dinner

To the Dinner class I added a new method called AddRSVP. By using terms from Domain Driven Design, the Dinner entity is the aggregate root of the RSVP entity. This basically means that a RSVP must belong to a Dinner; hence I want the Dinner class to control the list of RSVPs.

This change will help reduce code in the controllers and remove duplication. Previously the creating and adding of RSVPs was done both in the RSVPController.Register and the DinnerController.Create action methods. The Register method is shown below:

[Authorize, AcceptVerbs(HttpVerbs.Post)]

public ActionResult Register(int id) {

    Dinner dinner = dinnerRepository.GetDinner(id);

    if (!dinner.IsUserRegistered(User.Identity.Name)) {

        RSVP rsvp = new RSVP();

        rsvp.AttendeeName = User.Identity.Name;

        dinner.RSVPs.Add(rsvp);

        dinnerRepository.Save();

    }

    return Content(“Thanks – we’ll see you there!”);

}

Thanks to our new method, this can now be reduced to:

[Authorize, AcceptVerbs(HttpVerbs.Post)]

public ActionResult Register(int id) {

    Dinner dinner = dinnerRepository.GetDinner(id);

    dinner.AddRSVP(User.Identity.Name);

    return Content(“Thanks – we’ll see you there!”);

}

Similar reduction can be done to the Create method. BTW if you wonder what happened to the call to the Save method then we will come back to this in part 3 (teaser!).

As a rule of thumb always try to keep your action methods as simple as possible!

More object-oriented

The last change is the removal of the DinnerId property from the RSVP class. This property is purly database related, and does not belong in an object-oriented domain model. You can use the Dinner propety to get the DinnerId.

finally{}

In my opinion one of the benefits of using an OR/M like NHibernate as supposed to LINQ to SQL is that the domain model is much clearer. When trying to understand a new project one of the best places to start is by looking at the domain. I also did some changes (improvements?) to the domain model to better communicate intent, and to reduce code duplication.

The next step is to tell NHibernate how to map this domain model to the database. This will be explained in the next post where we look at the mapping.

If you liked this post then please shout and kick me :)

kick it on DotNetKicks.com Shout it

Tags: , , ,

10 Responses to “NerdDinner with Fluent NHibernate Part 1 – The domain model”

  1. Vincent says:

    Very interesting, what I would like to see is how you handle the SessionBuilder? Looking forward for the next parts of this post!

  2. Lars Mæhlum says:

    Very nice :)

  3. dusk.. says:

    When there will be a continuation?

  4. daoming says:

    Thank you very much. I am also looking forward for the rest of posts.

  5. Justin says:

    Great post, looking forward to the rest of the series!

    I wonder what you think of changing the Register action and pushing the if IsUserRegistered down into Dinner?

    Controller:
    public ActionResult Register(int id) {
    Dinner dinner = dinnerRepository.GetDinner(id);
    dinner.AddRSVP(User.Identity.Name);
    return Content("Thanks – we’ll see you there!");
    }

    Dinner:
    public virtual void AddRSVP(string attendeeName) {
    if (dinner.IsUserRegistered(User.Identity.Name)) return;

    RSVPs.Add(new RSVP { AttendeeName = attendeeName, Dinner = this });
    }

  6. bengtbe says:

    Thanks everyone for the positive feedback! I will try to post the second part in the beginning of next week.

    I wonder what you think of changing the Register action and pushing the if IsUserRegistered down into Dinner?

    Thanks for the suggestion Justin! I think this is a very good idea that will further reduce duplication and simplify the controllers. I will update the post :)

  7. Antonio Jr says:

    Congratulations!
    Very nice idea.
    Some applications become a reference to present a new framework. It seems that NerdDinner is the one for ASP.NET MVC.
    NHibernate + Fluent NHibernate + Linq to NHibernate are cool tools for the guys who are looking forward to create DDD like applications.
    Just waiting for the next post…

  8. Carl Hörberg says:

    Good article, waiting eagerly for the next part!

  9. Roselyn says:

    NHibernate is a very convenient way to use it in the domain. Less action and more results. I think your information will help a lot. Thank you.

  10. Model says:

    Nerd Dinner sounds like a pretty awesome site. I never heard of it before. The code is great, and will come to a dinner soon.

Leave a Reply