Sunday, June 03, 2007

A Journey with Domain Driven Design (and NHibernate) - Part 9

Back by popular demand… (and WAY too late, I might add…)

In this article, part 9 of the series, we’re going to wrap up our initial feature list and focus on building a user-interface for our video store, named Videocracy.

Here is our feature list:

  • 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 (move game, console, vcr, etc)
  • Rent an item to a customer
  • Check Items Back in
  • Get movies checked out for a customer
  • Query for an item, see who has it

Let’s implement Check Items Back In.

What is the use case here?  Typically a customer drops off a movie, either in the slot from the outside of the store, or directly to an employee.  Either case starts with the movie, or the Item.

So the employee is going to scan the item.  At this time, they are going to need to see the details of who and when the item was checked out (the Rental object), and then have a confirmation button to finalize the check-in (which will update the Rental object).

In order to facilitate scanning the item, we need to be able to query the database for a particular UPC.

In implementing this next test I noticed a large problem with my domain.  Our Item class defines a name property with a mapped column in the database.  The problem with this is that not all items have names of their own.  They might have display names that are a combination of other properties.  For example, a video copy of the movie The Matrix shouldn’t have a name of ‘The Matrix’ (it really belongs to the Movie class).  Typically the text on the outside of a video rental is the movie name followed by maybe the year, and the format that the movie is in.  These are deferred properties, so there is no more need for a database column in the item class.  I still provide a read-only Name property, and inherited classes have to provide this information.  Would you have made a large change like this so eagerly in your project?  If you did, how would you know if you broke some existing functionality?  As I always say, it’s a good thing I have the tests to back me up…

Here’s our next test:

      [Test]

        public void Can_Check_In_Item()

        {

            Item i = TestHelper.CreateTestItem("Test_ITEM123", "1234_UPC");

            Customer c = TestHelper.GetTestCustomer();

            Employee empl = TestHelper.CreateTestEmployee();

 

            using (ISession session = SessionSource.Current.GetSession())

            {

                using (ITransaction tx = session.BeginTransaction())

                {

                    //store the customer and employees

                    session.Save(c);

                    session.Save(empl);

 

                    //check the item out to the customer

                    Rental r = new Rental(i, 2.30f);

                    RentalTransaction trans = c.CreateTransaction();

                    trans.Employee = empl;

                    trans.AddRental(r);

 

                    session.Save(c);

                    session.Save(trans);

 

                    tx.Commit();

                }

            }         

 

            //item is checked out, can we check it back in?

            Item i2 = Repository<Item>.FindSingleByProperty("UPC", "1234_UPC");

            Assert.IsNotNull(i2); //just a sanity check.  Item exists in the table.

 

            //we need to get the rental by the item upc

            Rental r2 = new RentalFinder().FindByItemUPC(i2.Upc);

 

            Assert.IsNotNull(r2, "Rental was null!");

        }

There is a lot going on in this test, so let’s break it up and take a look at each part.

            Item i = TestHelper.CreateTestItem("Test_ITEM123", "1234_UPC");

            Customer c = TestHelper.GetTestCustomer();

            Employee empl = TestHelper.CreateTestEmployee();

 

            using (ISession session = SessionSource.Current.GetSession())

            {

                using (ITransaction tx = session.BeginTransaction())

                {

                    //store the customer and employees

                    session.Save(c);

                    session.Save(empl);
                    . . .

Here we setup our environment.  We have to have a lot of entities that already exist in order to test our new funtionality.  We need an item, a customer, and employee, and an account.  We create all of those and save them.  (Remember all of this happens within a transaction that is rolled back, so this doesn’t stay in the database).

 

                    //check the item out to the customer

                    Rental r = new Rental(i, 2.30f);

                    RentalTransaction trans = c.CreateTransaction();

                    trans.Employee = empl;

                    trans.AddRental(r);

 

                    session.Save(c);

                    session.Save(trans);

 

                    tx.Commit();

 

Here we setup the rental and perform the rental transaction.  This is how the UI will structure the business process.  Once everything is saved, it’s time to verify that we can retrieve the data solely based on the item’s upc code.

            //item is checked out, can we check it back in?

            Item i2 = Repository<Item>.FindSingleByProperty("UPC", "1234_UPC");

            Assert.IsNotNull(i2); //just a sanity check.  Item exists in the table.

 

            //we need to get the rental by the item upc

            Rental r2 = new RentalFinder().FindByItemUPC(i2.Upc);

 

            Assert.IsNotNull(r2, "Rental was null!");

The Rental Finder class will encapsultate common queries so that we can reuse them across the application.  Here’s the code for that method:

public Rental FindByItemUPC(string upc)

{

    Rental r = Repository<Rental>.FindSingleByQuery("from Rental r where r.Item.Upc = :upc and r.DateReturned is null", new Parameter("upc", upc));

    return r;

}

There is a lot going on here in this test, but what you need to get from this is that we are setting up the stage for our scenario.  As our tests get more involved we are verifying that business cases are being met.  If we make drastic changes later on, we will know if we have broken existing business functionality.

The next thing to check is to make sure that we can check the item back in.  I add a .Return() method on the rental, which doesn’t exist yet, so I need some additional tests.

I also need to be able to calculate late fees in a central place to make that easy to change later.  I add a few tests for this as well.

It turns out that the Return() method is easy to implement.  All we need to do is set the return date and calculate the late fee and save it.

[Test]

public void CanReturnItem()

{

    Rental r1 = GetTestRental(DateTime.Now.AddDays(-6));

 

    r1.Return();

 

    Assert.IsTrue(r1.DateReturned.HasValue);

 

    //it's 1 day late, so expect the right late fee

    float lateFee = Utility.LATE_FEE_PER_DAY;

    Assert.AreEqual(lateFee, r1.LateFee);

 

}

which leads to the following code in the Rental class…

public void Return()

{

    this.DateReturned = DateTime.Now;

    this.LateFee = Utility.CalculateLateFee(_dateDue, _dateReturned.Value);

}

Now we need to finish the original test and verify that we can save the rental.

 r2.Return();

 

 //save the rental

 using (ISession session = SessionSource.Current.GetSession())

 {

    using (ITransaction tx = session.BeginTransaction())

    {

        session.SaveOrUpdate(r2);

        tx.Commit();

    }

 }

This test passes and we’ve implemented our feature!

I think I’m at a point where I have demonstrated how we can work on core business features for an application test-first, using NHibernate along the way for persistence.  A lot more work has to be done to complete our domain model, but that will be left as an excercise for the reader.

Instead, I would like to focus my efforts on getting a basic UI in place using ASP.NET.  I said in part 1 that I wanted to demonstrate how to work with the NHibernate Session in a web environment, so that’s where I will pick up next time.

Until then, you can download and view the current project here:

File Attachment: Videocracy_09.zip (3442 KB)

Wednesday, May 30, 2007

Microsoft Surface

http://www.microsoft.com/surface/

This is so cool I just had to blog about it.  Take a minute to watch the videos, they are quite impressive.

Basically it's a 30 inch surface that is monitored by tiny wireless cameras.  Hand gestures can rotate and resize images and videos and things.  I don't think we're at a point where we can give up the keyboard or the mouse, but think of some of the other uses it would be good for.

In the videos they show a simple paint program that kids would enjoy.  They also show it at a restaurant where the menu is all digital and you flip through it using your fingers.  When you're ready to order, you just drag the item to the center.  The same would go for paying the bill.  Set your credit card on the table, split the checks up, calculate the tip, and charge the card all without waiting for a server to do it for you.

I think we're going to see some really cool uses for this thing outside of normal computing.

Thursday, May 24, 2007

Slaying the Six-Headed SOA Beast

For the past several years people have been hearing about Service Oriented Architecture through various mediums across the internet.  Most of what you would read would be academic material that was difficult to think about in concrete terms.

Still today there are numerous articles and books on the subject that talk so much in theory that there isn’t enough good direction on where to go.  For Joe Developer at Company XYZ, how can he take advantage of these seemingly great ideas on his next .NET (or whatever) project?

Unfortunately, this lack of good direction has led to a misconception about what SOA is and how it should be implemented.

At its heart, SOA is the next evolution of development.  Just as we had objects to encapsulate our behavior and compose applications of objects, we now can encapsulate core functionality as services so that they are reusable across the enterprise, or even to our customers. 

We can also take these services and compose them to create more complex services, and this can be taken to the extreme where you model every process that the business performs as a service.  The idea is that new applications can be built without the need to deal with implementation, it can be done completely by composing services.  Want to change the way the business functions?  Rewire the business process services in a way that better suits the business.

Unfortunately, when you talk this high level, you often gloss over the technical guidance and people take the advice and run in any direction.  It’s like a scavenger hunt where the treasure map is handed out and it contains details about the glorious treasure, but not the direction on how to get there.

This also leads to the misconception that SOA == Web Services, which is not the case.  More on this in a minute.

So back at Company XYZ, Joe Developer is implementing his next project with an SOA.  His management has also read about SOA and knows that it’s the right thing to do.  In fact, they may even be pushing him in this direction.  He creates his business logic, he creates his UI.  Next comes the service layer.  Joe thinks to himself, what better way to implement services than to be the first consumer!  It will be perfect!  Then any new application can access the functionality and everything will be great.  I’ll have an SOA Seal of Approval on my app because I used web services and my management will be happy.  And my application will be unnecessarily SLOW.  And complicated.  For no reason.

The reality is that Web Services are meant for interoperability.  They are meant to be accessed across a variety of mediums and ports.  They are great for platform interoperability and cross-political communication between applications.  They are not suitable for inter-company, .NET to .NET interaction.  The overhead of WSDL, SOAP, XML, etc is wasted in this scenario.  Consuming a web service is not efficient.  To combat this overhead, it is advised to make your remote calls as coarse-grained as possible.  Chatty remote calls will kill an applications performance.  So now we are designing our (!) application around the limitation of a heavyweight service layer.

This is something that I see all too often.

UI
SERVICE AGENTS
WEB SERVICE LAYER
BUSINESS LOGIC LAYER
DATA ACCESS LAYER

 
A better solution would be to design your application the way that good software engineering principles have taught you.  Separate responsibilities.  Create a domain model that represents your problem domain.  Speak in terms of your domain.  Leverage object oriented programming.

Then, when management decides that the functionality that you have created would be suitable to expose outside the company, or to their Java project that is currently underway, you can introduce a service layer.  The service layer consumes your existing business logic layer.  The idea is to introduce a more coarse-grained interface suitable for remote calls.  You create Data Transfer Objects that aggregate your domain objects and are more stable for a service contract.

This is a much better approach, because we take into account the fact that we can expose the functionality of services, but we should not shoot ourselves in the foot to do so.

Now getting back to the misconception of SOA == Web Services.  Why is this so common to see?  First, the term Web Services is probably partially to blame.  A service can be native .NET code, it can be a remoting interface, it can be SOAP over HTTP.  A more effective service oriented architecture (especially one within an application) is achieved by choosing the best medium for the circumstances.

I think it’s time for an example.  Say we are writing an application that calculates massive financial data for our enterprise.  One component of this will calculate the exchange rate for many currencies.  Ignoring the specifics of the implementation, how should we reuse this component so that the entire enterprise can access it?  We want to take advantage of a plain .NET to .NET inter-process call because it’s the fastest.  We’d also like to expose this to other .NET applications within our corporation.  Finally we want to use this service in our Java system as well.  How can we accomplish all of these without just resorting to the lowest common denominator?

One of the great things that I learned while studying Enterprise SOA in Paris was what the IBM guys call a Service Component Architecture, or SCA.  This architecture is enables services to be composable.   The WebSphere Integration Developer emphasizes modeling all of your business processes as composable services.  Most of these will be Java to Java calls, but some may not.  What they do is have you design your interface using a GUI, which spits out the WSDL interfaced and the Java interface.  The best flavor of interface is chosen depending on the circumstances.

So we can solve the above problem by having one single interface, with both a .NET and WSDL versions.  The configuration can determine which is used to achieve the best results.

Hopefully I’m not leaving out some core SOA concepts (I’m sure I am).  What about you?  Do you see too many mis-uses of SOA like I do?  What other techniques can help bring the industry back to reality?

(I also must apologize for such a long-winded post.  My bus is stuck on an HOV lane (one single lane with walls) in rush hour traffic.  I’ve been here for over an hour already.)

Tags:
Tuesday, May 22, 2007

Dubbelbock TFS

I have gone on record and said before that TFS Source Control is a great step up from VSS, however I’d never want to be constrained to the IDE for my SCC tasks.  Granted, IDE integration is a nice feature, but it shouldn’t be your sole interface.

This is part of the reason that I love Subversion.  It has builtin command line support for the freaks out there (like me) who like to type.  TortoiseSVN gives us explorer integration, which is solid gold.  Then there’s AnkhSVN for the IDE integration.  See a pattern here?

So I just stumbled on a new TFS Source Control explorer plugin tool with a funny name.  It’s called Dubbelbock TFS and it was written by a friend of mine, Ben Day.

This is the kind of tool that will help bring the power of TFS source control out of the IDE and enable a much more usable and powerful experience.

So go out and buy a copy, it’s only $25 (I think).  And tell Ben I sent you.

Upgrade Issues Resolved

I think I have the upgrade issues resolved.

Hopefully now the comments will work!

Wednesday, May 16, 2007

Pardon The Dust

My blog has received a long overdue face-lift.

I was getting a little tired of the previous black background design and wanted something cleaner.  I also upgraded to dasBlog 1.9, which proved to be more than just a walk in the park.  Things are still a little broken around here, but I will get it resolved sooner or later.  I hope you don’t mind a few broken links and things while I settle this new design in.

During all of this I was really pondering switching to Subtext.  It seems like a equivalent stature blogging enging on the ASP.NET 2.0 platform, however it supports databases.  Das Blog is all about the file system, which makes it easy for people to host if they don’t have access to a database.  But since I have over 2 years of content up here, not only does it take a long time to transfer over ftp, but when I upgrade I cannot simply copy files over.  I have to be very careful and selective and I usually end up messing up something.  (This is why I always back it up before I start).

I know that database support is on the roadmap for Das Blog, but I don’t know how long it will be.  We shall see.

Until then, enjoy the new design .  Please let me know what you think in the comments!

Tags:
Monday, April 23, 2007

Paris - Day 1

My company, Sogeti, has sent me to the beautiful county of France to attend a global enterprise SOA training from some of the folks at IBM.

So far, France is absolutely gorgeous.  I braved the subway system with my friend Aaron Murrell, which was very humorous.  We got a bit turned around a few times, but we eventually found our stop.

Streets of Paris

old building

We noticed that people park crazy in Paris…

Smart car parking job

We went into Paris and tried to find a restaurant called Chartier, however we couldn’t locate the street.  A quaint restarant called Le Flash caught our eye and we walked in.  The owner greeted us with many french words (which I do not understand) and we had a fun time ordering our food and communicating with him.  The food was excellent, the beer was even better, and the owner even played Alan Jackson for us, ha!  I don’t care for country music, but it was funny and appreciated nonetheless.

Le Flash

We then took a taxi north to the city of Gouvieux where the corporate chateau resides.  It really is breath-taking.  It was built by Eiffel a long time ago, and is now home for our Capgemini University (well, the bar and dining area, that is).

Le Chateau

We start our training tomorrow and I’m sure our day will be full of introductions and hands-on training.

 

Tags: ,
Thursday, April 19, 2007

Review - Agile Principles, Patterns, and Practices in C#

Robert Martin has a great book here.  I just finished reading Agile Principle, Patterns, and Practices in C# (what a mouthful!). 
 
In this book you get a fantastic play-by-play of a typical TDD/pairing session, which I think is a great way to demonstrate the process. 
 
This was also the book that really got me to appreciate UML for what it is.  I'm typically the guy who knows the UML basic shapes, and draws a bunch of interconnected rectangles on a whiteboard to help me solve problems.  I was never a "fan" of UML because I always associated it with the monolithic CASE tools that use UML as the source for an architecture, and out spits generated code.  The book described what Martin feels are the essential (read: useful) components of UML and really abandons the rest.  He emphasizes throwing away your diagrams when you're done and encourages writing them on a napkin or a whiteboard, which is infinitely faster than, say, Visio.
 
The section on patterns is well written, and it is always nice to get refreshers on patterns that you might not know about or haven't used recently.  Again he emphasizes not to go crazy with patterns.  They are there when they help, but don't be shy about throwing them out when they complicate things.  Indeed, this book is agile.
 
The final section (or rather 1/3 of the book!) goes about a payroll system.  He does a bit of UML to get the basic idea in his head and then he goes straight to the tests.  He fleshes out the entire thing, right there in the text.  The model changes, the tests drive the design and behavior, and he frequently consults with his "Customer" (himself) over what the requirements are.  After he has a working model that is fully tested, he bolts on persistence using a custom ADO.NET pproach, which is difficult.  Seeing him do this reminds me of how I like to use NHibernate, but I found it difficult to repeat this approach using flat ADO.NET calls.
 
I'm thoroughly impressed with this book.  If any of the topics above interest you, then go pick it up!
Debt - Credit Card Consolidation - Money - Arizona Landscaping