Hanalei, Hawaii Tuesday, February 09, 2010

MVC Storefront, Part 9: The Shopping Cart

In this episode I dive into implementing the Shopping Cart in a basic way so I can run a spike to make sure my pattern can push data nicely back into the DB without problems. Previously, On The MVC Storefront Part 1: Architectural Discussion and Overview.

In this episode I dive into implementing the Shopping Cart in a basic way so I can run a spike to make sure my pattern can push data nicely back into the DB without problems.

Previously, On The MVC Storefront

 

You can watch Part 9 Here (27 Minutes, 32M)

Download the Code Here

I Ramble
Stuff's getting complicated here and as such it's getting harder to keep "a story" going. It's even harder to tell that story without rambling! You'll notice some rough cuts here - I have to keep the time on these down so I chop things up a lot.

Spike Time
Many people have been asking about CRUD and specifically mentioning that everything will likely fail when I write back to the DB. I will admit I'm not used to seeing mapping code in my project (it gets buried by the tool of choice) but I have to say I don't mind it one bit.

I have complete control over what gets mapped and how, and it's not in my way - so I like it. I still have not addressed concurrency yet - but I will in the future episodes when I tackle inventory.

Technorati Tags:


Justin - May 8, 2008 -

It may just be me but around 13:40 the video stops playing, I can still hear the audio but the rest is either blank or frozen on the ShoppingCartTest screen depending upon how I view it.

Josh Stodola - May 8, 2008 -

On my machine, at 13:40 it skips to 14:05 (using VLC Media Player)

Rob Conery - May 8, 2008 -

Crap - confirmed. I am recreating the WMV now - will have it up in 45 minutes (it takes a while). Doh!

Josh Stodola - May 8, 2008 -

It happens (especially with WMV). Just out of curiousity, were you doing anything else on your computer while it was encoding?

(PS: Your comment form needs a "Remember Me" checkbox)

Joe - May 8, 2008 -

Your test "ShoppingCartService_Should_Return_SingleCart_ForTestUser" doesn't actually test what you think it's testing. Since "testuser" doesn't actually exist ("testuser1" does) you are creating a new cart with that test instead of returning the existing one (the behavior is the same "nouser"). Your test should be checking that the cart you are getting back is the exact cart you are looking for.

Also, the behavior of your TestCartRepository and your SqlCartRepository are different. The SqlRepository returns all items in all carts while the TestRepository returns the same (different) 5 items (that all have the same ID).

Rob Conery - May 8, 2008 -

@Joe - thanks, good catch :). I'll fix. RE the behavior diff - I'll tweak that too :)

Ajay - May 8, 2008 -

Hey Rob,

Video is still frozen at around 14:30 onwards.

Keep-up the good stuff you have been doing.

Thanks.

Ryan - May 8, 2008 -

I'm not sure if this is the problem that Joe mentioned or not, but the test SqlCartRepository_Can_Add_Product_3() fails after the first run because the test item remains in the SQL database, while the test checks for a count of one.

If you take a count before adding, you can make the test work by changing the assert to Assert.AreEqual(productCountOrginal + 1, productCount), but this leaves the new item in the database on every test run. I'm not sure if this is the desired behavior. Should you clean up after your tests so the database is in the same state every test run?

Stephen - May 8, 2008 -

One comment, I'm about half way in and you are working with getting the cart repository to create a cart for you.. doesn't this sort of break the model where your application model is the one that is in control of creation?

It seems to me that you should be doing:

var newCart = new ShoppingCart();

cartRepository.AddCart(newCart);

?

Sirrocco - May 8, 2008 -

I'm curious about how you'll manage the ShoppingCart scenario on the Web. Will you load the ShoppingCart on every request ? or will you keep it in Session.

If you keep it in session you might run into trouble with the disconnected objects ? (might be mistaken)

And if you hit the database on every request - again might be a bit of a problem.

Hope you deal with this scenario in Part 10 :D.

Stephen - May 8, 2008 -

@Sirrocco, going off the pattern, the repository shouldn't be expected to handle the caching here.. since the data in the application model is supposed to be disconnected anyway, the service class that interfaces with the repository should handle any caching of that storage data.. at least, this is the way I see it because the repository here is the component thats interchangeable, and as such, should be expected to be lightweight.. the service class is that which expands the lightweight functionality into more of a complex model..

(which I think was what rob was sort of saying with his hose pipe analogy)

Jimit Ndiaye - May 8, 2008 -

I second Stephen's comment about putting the ShoppingCart repository in charge of creating carts. It's job should be simply persistence and retrieval. Additionally, you mayn't want to actual persist the cart until required i.e when the user wants to save the cart to complete the purchase later or when checking out (if the cart hadn't been saved previously). Otherwise you could end up a database littered with orphaned carts.

Additionally, creating a new datacontext for each CUD operation seems a little excessive and may bite you in the back later with performance issues. Instantiating a datacontext is an expensive task I'm told. Alternatively you could implement a unit of work pattern in which you pass around an object representing the unit of work (containing a data context in the case of Linq-2-SQL) then only call Commit (or submitchanges) when all pending changes have been registered with the unit of work, which can then be disposed of.

Something along the lines of:

Public Interface IUnitOfWork

Inherits IEntity(Of Guid)

Sub RegisterAdded(ByVal entity As IEntity, ByVal repository As IUnitOfWorkRepository)

Sub RegisterChanged(ByVal entity As IEntity, ByVal repository As IUnitOfWorkRepository)

Sub RegisterRemoved(ByVal entity As IEntity, ByVal repository As IUnitOfWorkRepository)

Sub Commit()

Sub Cancel()

End Interface

What do y'all think?

kYann - May 8, 2008 -

@stephen, i think you're right, the repository shouldn't be the one who create the cart.

@sirrocco, i know for sure that loading the cart on every web request doesn't create some performance issue.

The screencast was very speed compares to the other, and i think an explanation is missing about the cart being loading by the "username".

A cart can be created when you're not authenticated, so in this case you don't have any "username". So what really is the username ? a sessionid ? If so, you should rename it.

Thanks again for those screencast, i think they are very interesting, and i really like the repository pattern that you are using.

sirrocco - May 8, 2008 -

Thanks for the input , I wasn't saying that the repository should change in any way - it should remain "simple".

But I'm just having a "weird" experience at the moment , where the client insists on saving the ShoppingCart on every product added/removed(or quantity modified).

I just now looked a bit more at the project and I see that he isn't using the exact mapped entityes, but is creating new ones . So I guess it wouldn't be a problem with detached objects .(hopefully Rob will go into this once the project progresses. )

Benny - May 8, 2008 -

The username on the cart did throw me off too.

Stephen - May 8, 2008 -

I agree with the username thing on the cart being a bit odd, it seems to be that a cart shouldn't have any understanding of a username.. a cart should have an identity that can be linked to a session, and an order (which may well be related to a username if you have streamlined registration (ie, anon orders force you to simply enter a password since you've supplied an email already)).. as far something like amazon, where it watches a users cart, again the cart shouldn't be the one that see's the user.. just that theres a service that is active for logged in users, that watches cart sessions..

Rob Conery - May 8, 2008 -

@Ryan: >>>I'm not sure if this is the problem that Joe mentioned or not, but the test SqlCartRepository_Can_Add_Product_3() fails after the first run because the test item remains in the SQL database<<<

Yah - this is just a spike and yes I should clean it up - but it's not meant to be an automated test just yet. Probably best to clean it all up :).

@Stephen: >>>doesn't this sort of break the model where your application model is the one that is in control of creation<<<

Excellent point - will refactor this :).

@Jimit: >>>Additionally, creating a new datacontext for each CUD operation seems a little excessive and may bite you in the back later with performance issues<<<

Creating the context isn't all that expensive, but your point is well taken. If I don't do this, however, object change tracking will cause a memory leak ;).

@kYann:>>>A cart can be created when you're not authenticated, so in this case you don't have any "username". So what really is the username ? a sessionid ? If so, you should rename it<<<

The username can be anything, and I don't have a requirement just yet for an anon user :). Oh hey - the client just called and asked for that! In this case I'll use a GUID and assuming at some point I'll recognize them, I can swap the GUID as needed.

@Stephen: >>>it seems to be that a cart shouldn't have any understanding of a username<<<

There are so many different ways of handling carts; the best I've used is no cart at all - just start filling out an order :) and set it to "not checked out" - which is what a cart is anyway.

Your point is taken though. We need to do customer recognition (membership) so the concept of userName here is important - but I like your idea RE Amazon. Can you send me an email with some more info?

pmw - May 8, 2008 -

Rob, can you explain what you mean about the memory leak with object change tracking? Are you referencing some known problem in LinqToSql, or some pattern that is known to cause a memory leak?

Rob Conery - May 8, 2008 -

@pmw: "Memory Leak" was too strong - no there isn't a hole in LinqToSql; rather if I was to leave ObjectTracking on with a Singleton pattern, it would track each instance I made of an object, and eventually I'd run into a problem with it trying to track all of em :).

ObjectTracking is there to handle concurrency issues - so if you pull a record from the DB, it will store it in it's tracking store, and you can change it, tweak it, do what you want and it's always tracked. Only when you use "SubmitChanges" does it go back to the DB with it.

So you can imagine if I have a really busy site and one singleton context. The more carts/items/products etc you create, the more it has to track and will hold all of them in memory.

Probably not a huge issue - but if you leave it long enough, it will be.

Doug Mayer - May 8, 2008 -

I'm loving the screencasts, but mark up another person who'd like to see some coverage of authentication and authorization in the coming shows. :)

Moses - May 9, 2008 -

I realy hope this is a hobby project and not a best practice for other developers, if so then sorry but you really need to learn OOP for real and unittesting etc. There are many good books out there. When i saw your code you broke atleast 5 OOP and code principles.

Robert - May 9, 2008 -

@Moses: Could you explain these broken OOP principles?

Fredrik Norm&#233;n - May 9, 2008 -

Why do you test your Mock Repositories?

[TestMethod]

public void ShoppingCartRepository_Should_Return_Carts() {

IShoppingCartRepository rep = new TestShoppingCartRepository();

IList<ShoppingCart> carts = rep.GetCarts().ToList();

Assert.IsNotNull(carts);

}

They only give you your predefined objects. So what you basically test is to see if C# will work, just a thought ;)

Your ShoppingCart have an Items property, why not use it to add your products and pass the ShoppingCart to the Service only? Is there any reason why you pass both the ShoppingCart and the Item downs to the Repository and then bind them together, maybe I missed something?

Product product = catalogService.GetProduct(1);

ShoppingCart cart = cartService.GetCart("testuser");

var shoppingCartItem = new ShoppingCartItem(product, 1);

cart.items.Add(shoppingCartItem);

cartService.Save(cart);

My last comment and it's about this code:

ShoppingCartItem item = cartService.FindItem(cart, p);

You already have the Cart, so why do you pass it to the Service and let it find an Item based on a specific product? Why don't you use the Cart's Items property instead (You can get the items from the cart when you already have the cart)? An entity like ShoppingCart holds its state. You can then add logic to the entity that will work against its state; in that case you don't need to pass it to the Service layer.

ShoppingCartItem item = cart.Items.WithProduct(product).SingleOrDefault();

When looking at your code, it's more functional oriented than object oriented programming, but there is a reason for everything, so you probably have a good reason for that!?

Fredrik Norm&#233;n - May 9, 2008 -

@Moses: The interesting thing is when we use TDD, we can end up without using any OOP at all, and the result can be simple old functional programming. It's the test that drives the design. But I agree with you about the OOP stuff, but there is a reason, and maybe TDD is just that kind of reason..

Moses - May 9, 2008 -

OO and OOP are from the begging referred to a real world object mapping to classes. Take this line of code:

ShoppingCartItem item = cartService.FindItem(cart, product);

When you are shopping on a mall with a cart, do you seek in the cart or let something else seek in it for you?

var product = cart.FindItem(product) <-- this is how it should be.

You check the cart for the item. This is OO design with OOP.

What about a car? CarSercvice.StartCar(landrower); Why? The car shall have the responsibility to start. What a bout an elevator?

Foo.GoDown(elevator);

Foo.GoUp(elevator);

This approach is more functional than OOP.

This is more accurate in an OO world.

landrower.Start();

elevator.Up();

elevator.Down();

I also pointed out other principles and can mention some. YAGNI (you aren't gonna need it), KISS (keep it simple stupid), DRY (Don't repeat yourself), SrP (Single responsibility Principe).

When using Robs stuff you need to do lots of thing all over again and got lots of services that aren't needed etc.. The repositories in his design always need services to work in a shared environment. The main reason of repositories (as the repository pattern are defined) is that they don't make you write redundant code if reused in other systems. In this case, you can't do much with the repositories; you lay too much repository-specific code in services so in fact your services are some kind of spaghetti of repositories rules and services rules. That make the whole design messy and that's when you broke some of the principles for others and yourself. (Can talk about this in hours...)

Regarding that TDD make code be more functional? I don't agree there. TDD is a more outside in design. You start with the outside and then build the inside. You can very well do OO and OOP with TDD. As with the shopping cart.

You test the cart and the carts methods as for the FindItem method. That's the one of the nicest thing with TDD. But then you must know design too, TDD don't make good design it's up to you to made it. And thanks to the refactoring-often-part you simple redesign it to more OO/OOP if you want too and know how too. To design a system need design-skills.

As a known developer once said:

"Any fool can write code that a computer can understand.

Good programmers write code that humans can understand."

Fredrik Norm&#233;n - May 9, 2008 -

@Moses:

about "Regarding that TDD make code be more functional.", No, but it can, I never ended up with it though. I read a book once where they used TDD with Refactroing, which ended up with no OO at all, but a small notepad app ;)

I agree with you about the OOP etc. Wrote about it in a comment earlier.

Robert - May 9, 2008 -

@Rob Conery (or someone else who know it ;) ) I have a question about your definition of the repository/the data project and the service.

In your GetCategories-Method (in the CatalogService) you wrote this:

IList<Category> rawCategories = _repository .GetCategories().ToList();

After that, you create a parents object (with some linq logic) and created child objects (ParentCategory -> SubCategory).

Why you do this stuff in your Service Layer? I thought the Service layer would just call the repository and the filters - no real logic at all.

Thanks for your help :)

Dietrich - May 9, 2008 -

@Moses. Your point is well made. But isn't a lot of your argument part of the refactoring process? Thus you're jumping the gun a little bit? Personally during the development period, I find being a OOP purists detrimental. For example, some concepts aren't easily discernible as "objects" and two your objects are still malleable and changing. Trying to be a OOP purists during this phase (at least for me) can lead to a lot of unneeded code and at worst badly conceived and thus written objects.

To me the least amount of code is the most readable code --especially during development.

Moses - May 9, 2008 -

@Dietrich

I will use the word Maybe here as an answer and the bad explanation "It depends"

I don't get that problem you mention anymore (or at least not often.). I use SCRUM as methodology and Domain Driven Design with TDD so I got a very clean and wide model for my development procedure.

In a simple point of view: (as is)

I got my storyboards in a nice prioritized order, translate my substantives to classes and verbs as methods, works about 99% of my cases. And then I use TDD for each substantive (my classes) and unit test my verbs (methods). I also mock if I need to split em to more classes and then create those classes. Thanx to short sprints (iterations) I can change my model fast if needed.

And thanx to DDD o got a clean design with classes that have its responsibilities. Like. Entities, Value objects, Services (not service layer classes), Factories, Repositories (as they are specified, not as Rob does ? change the name and I got happy ? )... And the story tells me very fast what kind of DDD class type it is and therefore I got a nice OO and OOP from the beginning.

It sounds crazy I know, but since I started using DDD and SCRUM I got really effective, with some principles in mind like. YAGNI,KISS,SrP,DbC (design by contract)... Before I used DDD and had my own way to structure my code and I got into the problems you mention very often. Ok this sounds like a Silver bullet and I think it is for me in a design point of view...

Rob Conery - May 9, 2008 -

@Fredrik: >>>Why do you test your Mock Repositories?<<<

I want to be sure that they are working like I expect. I know it seems like a silly test, but you wouldn't believe how many times I've inverted an IF and a core routine doesn't work.

>>>Your ShoppingCart have an Items property, why not use it to add your products and pass the ShoppingCart to the Service only<<<

I definitely can, yes.

>>>Is there any reason why you pass both the ShoppingCart and the Item downs to the Repository and then bind them together, maybe I missed something<<<

In this first pass I just want the product added to the cart, so I send both to the service.

>>>so why do you pass it to the Service and let it find an Item based on a specific product<<<

I don't want logic in my cart (my preference here - your idea would work as well).

@Moses: >>>I realy hope this is a hobby project and not a best practice for other developers<<<

I'm only going to ask you ONE more time to raise the level of your comments. If you have some thoughts, share them. I don't appreciate trolling.

>>>When you are shopping on a mall with a cart, do you seek in the cart or let something else seek in it for you?<<<

I could ask you the same: Do you ask the cart to hand you that box of Lucky Charms?

>>>You check the cart for the item. This is OO design with OOP.<<<

If I was being strictly OO here my app wouldn't scale. I have this routine in place as I want the repository to find the item - not the cart.

>>The car shall have the responsibility to start.<<

Disagree. Driver.StartCar(car)? :)

>>>When using Robs stuff you need to do lots of thing all over again and got lots of services that aren't needed etc<<<

If we're using ActiveRecord then I'd be more free to use Data-aware objects. I'm not - but this has nothing to do with OO.

>>>As a known developer once said:<<<

Keep these to yourself please.

@Robert: >>>Why you do this stuff in your Service Layer?<<<

This is where the business logic lives. Grouping the categories into a tree is an app concern, not a data one so I chose to put it there. This free's up the DB design a little.

Stas - May 9, 2008 -

Rob, this is not really related, but I would like to see this, i've also emailed you regarding the problem i have.

Are you aware of LINQ to SQL generated sql statement that is INSERTing record on table with XML data type.

I get this error, and i cannot find any answers at all.

Msg 305, Level 16, State 1, Line 2

The xml data type cannot be compared or sorted, except when using the IS NULL operator.

If anyone has any ides, what is the problem or how to fix it, please respond.

Thanx in advance.

kYann - May 9, 2008 -

I think we are having a SOA vs DDD debate here ?

One interesting point is this one :

>>>Your ShoppingCart have an Items property, why not use it to add your products and pass the ShoppingCart to the Service only<<<

So there is two possible way to handle this :

cart.items.Add(shoppingCartItem);

Would be the simpliest way to do it, and certainly the natural way to do it.

But rob choose to do it using a service :

cartService.AddItem(cart, p, 1);

To my point of view, rob's way is the "smarter" one of the two, because if you need to do something more when adding a product into the cart, then it's doesn't create any dependency on the cart with some other stuff (it could be discount stuff for example or maybe his client wants him to decrement the stock when the customer add the product into the cart).

Anyway, i'm not a big fan of SOA because it's a litte bit far from OO design.

I would certainly create an AddItem method on the cart that would throw a static event before and after adding a product, so anyone could do the things they need to do when an item is added.

Fredrik Norm&#233;n - May 9, 2008 -

@Rob:

I don't see why the building of an Entity with its aggregate and value object is business logic. In that case if we use DataSet, we should add all columns, rows, tables in our services (The Fill method which fills the DataSet with a table will have "business logic"), and think of it, everyone that want to get a DataSet need to implement this logic into a service, it will let them repeat them self, so why not just encapsulate it into the "Repository"? For example a Repository of Humans has the responsibility to give us Humans as objects, in this case Entities with all its aggregate and value objects. Aren't your service like a factory, it takes the arms, legs, heads etc and put it together to a single complete item (It creates the Human). Every service that want to use your "Repository", need to do the creation of the Human, this will let them repeat them self and they need to know that they have to do chatty calls to the "Repository" to get the different items and then they need to know how to put them together. An aggregate root (entity) is for me a well structured data component that holds data in a structured and logical way, and that is the item that comes from the "data layer". It's another way of creating a container of data, like a result set such as DataSet is one way to return data.

BUT! A BIG but.. When returning an Aggregate root, we need to have a way to handle performance issues, for example using LazyLoad, or make sure we create one service for each specific Use Case which can construct the Entities with the data the Use Case only need. I often use Eager Loading with Load Spans, but LazyLoad may occur. In some scenarios I also remove my aggregates from an entity to KISS, but I make sure that the entity doesn't have a property which is null or empty, even if it has data. This will only confuse the people that want to reuse my code.

@ kYann:

>>To my point of view, rob's way is the "smarter" one of the two, because if you need to do something more when adding a product into the cart, then it's doesn't create any dependency on the cart with some other stuff

Good point, but this can be handled within his Service even if he passes the whole ShopingCart to the Service also, but a call to the service is needed every time the Cart is updated.

cartService.Save(cart);

I build a enterprise B2C app for some years ago where we decided to use that approached, but sometimes people only wanted to put stuff into the cart to get a summary of the price before they wanted to do the purchase. In that case it can be a problem to remove items from the stock when it's added to the cart.

>>I would certainly create an AddItem method on the cart that would throw a static event before and after adding a product, so anyone could do the things they need to do when an item is added.

This is something I often do, only to make sure others can subscribe to changes made to my model.

Moses - May 9, 2008 -

>I'm only going to ask you ONE more time to raise the level >of your comments. If you have some thoughts, share them. I >don't appreciate trolling.

I already have so am others, but you don't listen. And it scares me. So I got upset and was trolling!!!

>I could ask you the same: Do you ask the cart to hand you >that box of Lucky Charms?

Depends on the abstraction level yes. I never ask a thingy to get my thing from my things by a thing. For many reasons.

1... Abstraction level, why add extra classes when not needed? Why let a thing take two things to see if the one thing got the other thing?

2...It gives you more code than needed and breaks the KISS principle.

3...More code gives you more tests, it gives you bigger chance to create bugs.

4... It gives you more classes and a more complex system when not needed if doing it OO style and not functional style as you do in this case.

I only give you my thoughts here; you don't need to agree with them. But I'm glad you explained that you weren't OO strict. Because that was my point.

>If I was being strictly OO here my app wouldn't scale. I >have this routine in place as I want the repository to >find the item - not the cart.

I don't see your point here. Why can't it scale when doing OO/OOP? That's one big benefit OOP really can give you.

>Disagree. Driver.StartCar(car)? :)

And the StartCar method calls the car.Start? Or is the driver the engine too? ;)

>If we're using ActiveRecord then I'd be more free to use Data-aware objects. I'm not - but this has >nothing to do with OO.

I'm confused here, ActiveRecords as Folwer explains it or your own definition of it? Though you have your own definition of repositories so it's kind of hard for me to understand if your ActiveRecord is that ActiveRecord? Ok I'm joking, but you see my point here regarding the use of names for things that's not, it confuses ppl.

It's like calling a mouse the keyboard or a White Russian a glass because you need a glass to it if you see my point?

D'Arcy from Winnipeg - May 9, 2008 -

@Fredrick

"For example a Repository of Humans has the responsibility to give us Humans as objects, in this case Entities with all its aggregate and value objects"

No...the repository is responsible for returning data from a data store...the domain objects are responsible for modeling the business domain...and the service is responsible for putting it all together. Having the repository do that would be a big violation of SoC.

"I build a enterprise B2C app for some years ago where we decided to use that approached, but sometimes people only wanted to put stuff into the cart to get a summary of the price before they wanted to do the purchase. In that case it can be a problem to remove items from the stock when it's added to the cart."

I gotta ask: why would you alter your stock levels when people are putting things in their cart? Technically those items aren't out of stock until they've actually been processed as part of a sale.

@Moses

"It gives you more classes and a more complex system when not needed if doing it OO style and not functional style as you do in this case."

In my experience, purist-type OO applications do NOT reduce class count and do NOT reduce complexity. In fact, they increase: business logic is typically placed in heavy entity objects which become test-nightmares and unruly as more functionality is added.

You also mention that OO/OOP can help scale...this can also be a misnoamer. OO is typically *not* a better scaling/performance choice because of the extra overhead of using objects compared to the more functional style (and I'm not convinced that using service classes to handle logic is necessarily more functional than object oriented).

Your argument seems to be from a more purist OO background, which I can appreciate...the same way I appreciate some PM's and BA's still express praises for Waterfall...but that doesn't mean its where the industry is going. ;)

D

Rob Conery - May 9, 2008 -

@Moses: I don't listen? Interesting. And here I thought I was pretty good at that. Your thoughts on OO are very good and believe it or not, I've actually learned a few of them over the last 28 years of my professional life. I'm happy to debate what I'm doing here - but it's sort of taking things down to a pretty basic level to be honest.

Your example of Car.Start() is a perfect example of what I'm talking about. A car doesn't start itself - you do when you turn the key. Is this unfair? Sure - it's reality :).

No system is as simple as what you're suggesting, and it's why we have these architectural discussions - because OO in and of itself is a principle. Implementation of it is the fun part.

Moving on...

Overall the argument whether something is OO or Repository or proper SOA is fine - but please know that the point of this screencast was to spike SQL server. I mentioned a few times that what I was doing would be refactored - and I still plan on that.

RE what puts items where - you have to pick what you're doing and move with it. I don't want that logic in my cart; though it's perfectly fine to have it there.

If I did that - Cart.AddItem() I would need to access the Repository from there - and I don't want that. There is a lot of logic involved in adding an item - Inventory is a valid concern - so are discounts and other things. By putting this in your Cart object you're now making your Model responsible for implementing it.

What I'm trying to build in here is something of a Broker - I have a cart and I want to put something in it Mr. Broker - can you scan handle this for me?

It's a perfectly normal pattern - and isolates your logic in one spot so you're free to use whatever Cart system you like (which includes 3rd party).

Finally - as I said in the screencast - I didn't implement a Cart in the CSK - we used Order and OrderItem and simply set the status to "NotCheckedOut". This allowed the transaction to revolve around the authorization and the changing of the status field (less moving parts). It also allowed for some nice reporting.

Fredrik Norm&#233;n - May 10, 2008 -

@D'Archy:

Can you let me know why it's a violation of SoC to return a logical model of the data? The Infrastructure layer used by the Repository returns the data from a data source, the Repository is a repository over the Entities. Think of a book shelf, you get a complete book from the shelf, not chapters that you need to bind togheter each time you want a book. You can read about the Repository and Entity in Fowlers or Evans book, there are good examples also and you will get a glimpse what I'm talking about when I refer to the Repository.

"I gotta ask: why would you alter your stock levels when people are putting things in their cart? Technically those items aren't out of stock until they've actually been processed as part of a sale."

The Customer of the product wanted the app to work like a store in the real world, where people put items from the shelves into a cart. In that case there is one less item left in the shelf for someone else to pick.

@Rob:

Thanks for your answers. There are absolutely different ways to solve things, for example if we use Agile, YAGNI and KISS, it will end up in different ways.

Robert G - May 10, 2008 -

Rob, these series have been great. You are an awesome speaker and your thoughts are well organized and really easy to follow. I learned a lot from you.

The only thing I'd request, if possible, are higher resolution videos.

Moses - May 10, 2008 -

@D'arcy

>In my experience, purist-type OO applications do NOT >reduce class count and do NOT reduce complexity. In fact, >they increase: business logic is typically placed in heavy >entity objects which become test-nightmares and unruly as >more functionality is added.

True, it can, but it depends, for example if using ActiveRedords it really got big, I'm not a fan of ActiveRecords I like the repository pattern here (the Folower definition of it.) It reduces lots of code in the more upfront code etc... Entities don't usually have that complicated business rules or logics (I refer to DDD now) other classes with the right reposnsibilities do though.

>You also mention that OO/OOP can help scale...this can >also be a misnoamer. OO is typically *not* a better >scaling/performance choice because of the

But scaling and performance aren't the same. I can agree with you that OO/OOP can decrease the performance but it does not make it less scalable. Our computers are so fast now so you typically need to code wrong to get bad performance but make code scalable is more a design issue.

@Rob>If I did that - Cart.AddItem() I would need to access the >Repository from there - and I don't want that.

That's true if you use ActiveRecords. But in my case I was referring to the repository pattern and that's why you don't need active db logics in your cart.

You simple only add items to it and fill it with items; this is what the repository does for you.

E.g a cart uses an List<Items> where you add your items. The repository fills it for you.

Cart carts = cartRepositort.GetCartByUser("Rob");

Then if you need to know the price and total of the items you got very little business rules in the cart entity that loop through your items and calculate their price etc.. (You don't ask the DB for anything here either though you got your items in the internal List<Item> inside the cart entity.

When you need an item you ask the cart for it, loop the items to see if it exist. That's what I was referring too; sorry I wasn't clear from the beginning.

When saving you send the cart that has it items to the cartRepository SaveCart method. The save method get your filled/changed items and saves them as with the cart info to the data source.

In this case developers use the cart and the item entities in the UI and pass it to the Service layer that then pass it to the repositories for saving it. (Other things might happen on the way though). When the UI want a person's cart it ask the Service/application layer for it and you return a cart entities with the items. You never need to ask the DB for an item in the cart though it already there. For performance issue if got over 100 000 items you simple use the Lazy Load pattern instead.

Thanks to this you reduce lots of unnecessary db access codes in service layers and repositories etc. You don't need to access a db to get an item from your cart though you cart already have them.

Mike - May 10, 2008 -

So why not just update the quantity property of a shoppingcart item, and then say that's what you want to save? It seems a lot of work so pass an item (shoppingcartitem) and a property (quantity) explicitly to a method that will then 'sync' the stuff. Why set it in your model and pass it to the method to update?

PS. It's going a bit fast...

Mike - May 10, 2008 -

Ì have never done testing (or TDD) but this is not making sense to me. Often it seems that A) it's testing C# and .NET and B) it's testing the database.

For A) I believe that's already tested. And for B) the bigger the app gets, the longer the tests will take, plus I think it would be better to do it in a transaction and roll everything back when the test is finished.

James - May 11, 2008 -

Hi Rob, Should we still be able to access the source code over SVNBridge? I've been getting an error for the past 24 hours?

King - May 11, 2008 -

I just downloaded the source code for the entire project and opened it in VS 2008 and the two Test Projects couldn't open. It said the project types weren't supported.

I know I'm missing something. Any ideas?

David Alpert - May 11, 2008 -

@Rob,

I'm curious about your take on @Mike's suggestion to confine your Sql-touching spike tests inside transactions so that (theoretically) you could run them on your actual data without polluting it. Also, you could then (theoretically) run the test multiple times without changing your data and breaking your tests.

Secondly, i appreciate the quicker speed at which you are moving with these webcasts, however i do have a request - would you please show us a new test before new code?

I don't need to watch you type every line, but seeing code before testing loses the feel of test _Driven_ design. Watching the code unfold is helpful as it let's me follow along with your thought process. I can write code, but i'm hoping that if i watch how you construct your tests, and how the code flows from them, then it might help me do the same.

Moses - May 12, 2008 -

@Mike

>For A) I believe that's already tested. And for B) the >bigger the app gets, the longer the tests will take, plus >I think it would be better to do it in a transaction and >roll everything back when the test is finished.

I can share my experience here.

The bigger the teststpropjects get the longer the test will take? that's true. But thanx to TDD you only or often test that unit you are working with at that moment. Then you can run all test to make sure you haven't broke anything else, but this "all testing" can often be made at the end of the day when checking in your code. That unit you work with at the moment is the unit test that matters most.

It's always a problem to test DBs, that's why you often mock the data access classes. And when testing db you can either use another tempdb for your data access unit tests so you don't need to care that much about crap data in the db. I often clean up the database with another test like my delete-tests not in transactions as you mention. It depends on the project size.

There is problem doing this too. You can't test some test before other tests have been made.

The risk of using transactions within a test is the green and red pattern. If you get an error you enter the callback state and it can give you a red pattern when clearing the data as well. So you need more units in a unit test, the unit got big and the idea of testing one unit for each unit tests fall apart as I see it.

Instead use a Set Up method and a Clean Up method that reset the database for you. How to setup those methods depends on what framework you are using. MS in VS .net or the NUnit framework.

Jimit Ndiaye - May 12, 2008 -

@Rob:

Referring back to your comment about object tracking and potential memory leaks... I didn't mean keeping the datacontext around permanently as you've done using the singleton pattern for your readonly context (which is an excellent idea btw - i'm using it in a project of my own ;)) but merely holding onto it until you're truly done with all CUD operations in the current operation and then discarding it. For instance if you're saving a new cart with several items, instead of creating a context for each insert, i'd pass a single context around for each individual operation, commit them all then discard the context.

On another matter, specifically your unwillingness to have such things as Cart.AddItem(itemToAdd) - in a perfect world our domain would be entirely persistence ignorant and would not worry about such things as whether when you add an item to the cart, the database gets synced to reflect the change immediately or not. One solution is to mark the added item somehow as being transient and then have your repository figure out how to save the entire object graph. But one could argue that that's a form of persistence-awareness too. :)

Either way, thanks for a great series. Keep 'em coming.

@Moses: Could you please elaborate on the risk of using transactions within a test? I couldn't quite follow the red-green prb you describe and your inference that you'd need "more units in a unit test"

Rob Conery - May 12, 2008 -

@Jimit - Great point RE the context :). I'll refactor this to take advantage of the context lifetime.

It's not that I'm unwilling completely - @Moses has a point about "why do we need another class" in that the Service and Cart could really do the same thing. It sort of breaks the idea of a dumb model however :) which I think is what you're saying.

Either way - what I've learned here is NOT to charge ahead with anything until the idea's solid. This was supposed to be all about pushing data to the DB - not cart architecture :). I got lorem-ipsum'd :).

Erik L - May 12, 2008 -

Changing Inventory when someone places something in thir cart is certainly a valid practice. Any busy store where you have a whole bunch of inventory with low quantities(Dells refurbished store comes to mind) may frustrate customers if as they load up there cart, and they regularly have items unavaillable by the time they check out. What Dell does is that when you put a computer in your cart it is unavaillable for purchase but your cart times out in 15 minutes. I would guess that [InCart] is just another inventory status.

Erik

Moses - May 13, 2008 -

@Jimit

Maybe I got it wrong, I was tired when reading the post about transactions to restore database.

But instead of reading the post again to see if I got it wrong will only answer my thoughts at that time when I read it. Ok?

A unit test is testing a thing a unit so far everything is fine. But say you add a transaction inside a unit test (that's what I thought he mend when reading the post fast.) then you must handle a return of red or green pattern. With red and green pattern I mean red or green indication (pass or fail). The pattern is the indication of green (success) or red (failed) this is the idea of the test and that's the main pattern for a unit test. Red and green pattern (Kent Beck).

You do the DB test, you add it within a transactionscope it fail then it will call the rollback, but now you need to do something, you must tell the unit if it was a success or a failure. The Asserts explain it for you, but when entering the rollback phase you might need to add more code for the cleanup (it's not always true but might be the case in some scenarios and it's those scenarios I refer too. In the rollback phase you add new code you might call the delete method of your Data access class or a restore my db method. This method can also fail or succeed. So how can you be sure the cleanup works within your rollback? It's another unit here. The delete or remove or cleanup unit. This unit must work if not then you got a messy db anyway. But it all depends on how, when , what and depends here.

It's always a problem testing db what so ever, and there are many different solutions to test against a database. And there is always the same question all over again. How shall I test a unit test against a database?

Rob do a nice thing, he uses mock objects, he also test the mocks I can't really see the point of doing that, but it's ok a mock,stub or fake can also be changed and if so then we might want to know it. It's you decision to make.

The main problem is not often the database, it will be if the data base is your application, I try to avoid this and thanks to DDD it does. A system can work without a database the only problem is the dynamic data. You often test the static contracts. And the data access mocks do not need to be able to save or update or delete data. It's a separate testcase, the main goal of TDD is to know that your application, your businesslogic, business rules work. The databse is often like an airplane black box. It shall never be the main core of your application, the models are. The db only handle the state of your model. I'm talking about a full OO and OOP system now, not a functional system or transaction script (se fowler for more info regarding this) where you often get the data you want from the database and not from your model.

If you build a system based on a old functional way or a transaction script way you will get lots of problems using TDD though you must test to much in each unit. You do not really do a unit test in this case, you to a functional and process test. Your unit (as you might see it) uses other units to work. But building a system fully OO as DDD are you do not need to test the db to understand if your units works.

I don't now if this gave you any answer though I can't give you one though it all depends. But I hope this answer gives you some ideas and thoughts how you want to handle this. In my case I use mocks, I add a separate testprojekt fort my data access classes (my repositories) . And I have no real pattern for the tests here. In some systems I do the test in a order so I first test a write and then test a delete, I can't test my delete before I have tested my write. If I must test my delete separate I do reduant code in that test, I simple calls a write and then my delete. But I never use a transaction within my unit test project. Though the test will be to complex and can generate more problems that might

Moses - May 13, 2008 -

@Jimit

My endings was cut so here it is:

...Though the test will be to complex and can generate more problems that might end up to a new unit test of my unit test to make sure my unit test don't fail. And if you must do this you really got a problem....

Keith J. Farmer - May 29, 2008 -

Jimit is correct when he suggests tying the lifetime of a DataContext instance to a unit of work -- that is precisely what we designed it for.

It may be helpful to realize that the context's knowledge of the database is necessarily limited to the moment when the query occurred; data is immediately stale upon retrieval, and so there is no value (indeed, there is anti-value) in retaining the context longer than in necessary. So a task is a process which we consider to take place at a single instant of time, and we scope the context's lifetime accordingly.

In general, for a web application, that task should probably be no larger than a single HTTP request.

Rob Conery - May 29, 2008 -

@Keith: >>>data is immediately stale upon retrieval , and so there is no value (indeed, there is anti-value) in retaining the context longer than in necessary.<<<

You've pretty much put a nail right on why a lot of people don't like the DataContext approach. For example - why in the world would I need to track the 20 Categories and 10 Products that I pull to show on my Catalog page? There is no updating, no concurrency issues - and if there is, there's nothing I can do about it anyway right?

I'll leave it here WRT to the DataContext discussion. Suffice to say it doesn't like being relegated to a Repository role, and I've had some fun making it play nice :).

I understand what you're saying, however. It all comes down to me trying to break it's design :).

Tornaydo - May 19, 2009 - Hi Rob, I really love the work you've done, just one thing has started happening, as the architecture / design and code is getting more intense and complex, the TDD model is constantly shifting my focus from what your really trying to achieve "A StoreFront in MVC". In fact due to the unit tests I have to keep pulling the time line back to the point i got lost when you start showing the test code. I know this might sound weird, but I think that you did prove enough that the TDD model is very useful and I would love to use it myself to whatever practical extent possible but when i'm seeing it in a demo that you're making its confusing the hell outta me :o)
Gecko