Last time I talked pretty high-level about starting a project with NHibernate.
This time I am going to start off creating a pet project to demonstrate how to implement these ideas. First we’ll implement a simple domain model to support our features. We will combine these with unit tests to verify behavior.
Let’s create a dummy video rental system. We’ll call it Videocracy (hey, that’s pretty good!). In this sytem we’ll have to support the following features:
- Add new Customer / Account
- Add other members to an account
- Restrict certain members from renting certain content
- Query for a Customer by Customer # (swipe card, etc), phone number, or last name
- Add new rental item (movie, video game, game console, vcr, etc)
- Rent an item to a customer
- Check items back in from a customer
- Get movies checked out for a customer
- Calculate late fees for a customer
- Query for an item, see who has it
I think I will create this as an ASP.NET application to demonstrate some of the concerns with NHibernate in combination with the stateless nature of web applications. WinForms topics will be relatively straightforward in comparison.
Following domain-driven design principles, I want to develop the core functionality of the domain. I want to unit test it. And I don’t want it to be influenced by infrastructure concerns. Many people would start this project off by creating the database structure. I will put this off until much later so that I can not let a relational design influence my object-oriented design. The database supports my object model, not the other way around.
Designing the model
Ok, just about any system has users, and in this case there are a couple types of users. We’ll start by modeling what a “Person” means to our system. A person can represent a customer or employee, so we’ll join the commonalities in the Person class:
Employees will have additional data relevant only to them (I kept it simple):
And finally, the Customer class:
I decided to leave these pretty simple. There is no need to implement features unless there is an existing requirement for them.
Next up is the Account class. An account will have at least 1 customer. The account will also have an outstanding balance, so that an employee can ask for late fee payments when they rent new items.
Accounts will have current checked out items as well, so I modeled a simple Video Item. A video represents the physical item that is checked out, but each video is actually a movie, so there is a reference to a movie class as well. Games will be vastly similar, so I’ll omit it here. Here’s what the whole thing looks like now:
As you can see, the account holds a reference to its members. This is a generic list, and though NHibernate does not support it (yet) we can implement them using the excellent NHibernate.Generics assembly written by the very intelligent Ayende Rahien.
I’m not entirely happy with the domain model right now. I think that having the Account directly hold a reference to its currently checked out items is a code-smell, but I can refactor that a bit later. I think this kind of information will be better delivered by querying a service rather than contained within the object itself. (Making a note to refactor…….).
Next up, we need to model the actual rental structure. This is where the design will take the most pressure.
A customer grabs some movies and goes to the counter. The employee opens his account and starts a new Transaction. Items are scanned and added to the Transaction. The rental encompasses that dateRented, dateDue, dateReturned and late calculating logic. I will defer late fee calculation until later as a strategy class, as that is not the responsibility of a rental.
I decided to remove the reference to the ItemsCheckedOut of the Account class. This data is provided through the rental structure, and a simple query will return items that have not yet been returned. If I had already made this change in the database as well as the object model, it would be more difficult to change. If a change is difficult to make, developers are less likely to make the change.
I realize that we haven’t yet touched NHibernate yet, but I’m getting there. I want to demonstrate how NHibernate fits with Domain-Driven Design.
I think we’re at a point to start unit testing the behavior of the system. Next time I’ll introduce a testing project and go through some behavior tests.