NerdDinner with Fluent NHibernate Part 1 - The domain model

Monday, 10 August 2009 06:00 by bengtbe

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

Comments

July 23. 2009 18:24

trackback

Trackback from DotNetKicks.com

NerdDinner with Fluent NHibernate Part 1 - The domain model

DotNetKicks.com

July 24. 2009 13:13

Vincent

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

Vincent

July 24. 2009 13:58

Lars Mæhlum

Very nice Smile

Lars Mæhlum

July 24. 2009 15:07

dusk..

When there will be a continuation?

dusk..

July 24. 2009 15:09

daoming

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

daoming

July 25. 2009 08:35

trackback

Trackback from Travis.Net.Blog

NHibernate Talk 8/11/09

Travis.Net.Blog

July 27. 2009 08:16

Justin

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 });
}

Justin

July 28. 2009 00:02

bengtbe

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 Smile


bengtbe

July 30. 2009 07:04

Antonio Jr

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...

Antonio Jr

August 1. 2009 22:33

Carl Hörberg

Good article, waiting eagerly for the next part!

Carl Hörberg

August 3. 2009 13:55

Roselyn

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.

Roselyn

August 27. 2009 19:31

pingback

Pingback from nettehnologic.wordpress.com

Fluent NHibernate « Net

nettehnologic.wordpress.com

September 3. 2009 15:37

pingback

Pingback from nettehnologic.wordpress.com

Follow « Net

nettehnologic.wordpress.com

December 3. 2009 18:58

Model

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.

Model

Comments are closed