Home MVC Storefront

MVC Storefront, Part 6: Catalog Completion and Initial UI

This is a long one - 20 minutes to be precise. I usually try to edit things pretty liberally, but I asked Jon Galloway to help me out with CSS and there was so much good stuff in there I decided to keep it all :). I also finish off my test data Spike, and talk about recent feedback with respect to the "Repository Pattern" I'm using.

What's In a Name?
A lot evidently :). I've received a lot of feedback from the Domain-Driven Design (DDD) guys about my use of the "Repository Pattern". Phil warned me about this from the get-go, but I was hoping that my use of the CatalogRepsository would be viewed as acceptable given the changes to .NET with respect to LINQ.

The discussion's ongoing, and I address some of the points in this webcast.

UI Work Is Starting
The client is getting pushy and wants to see something! I told them the demo would be ready, hopefully, by Wednesday and I think we'll make the deadline - but you can never be sure. Like a good PM, I made the date up out of thin air without consulting with the dev (myself - it's a trick I learned a while back... talking to myself and forgetting it happened). Hopefully that guy can pull through for me :).

So I set the other half of the team (splitting myself into 3 now) to working up the UI bits. To do this, I asked Jon Galloway to set us down the right path with a talk about CSS and HTML.

The UI deserves way more time than I gave it in terms of discussion - I'm hoping to expand on it as time allows - your input is critical in terms of what you want to see this way.

LazyLists
Finally, Ayende dubbed me an "Evil Genius" for flexxing the framework to deliver the Lazy Loaded list - something that ORM tools have had to make up for themselves up until now (with the intro of IQueryable). He spent about 30 minutes with me, going over the architecture of the solution, and then came up with what I think is a pretty cool List class (it's in the video).

Not sure about the "genius" part of it - that's Oren, not me :).

Watch Part 6 Here(20 minutes, 30M)

 

Technorati Tags:
Chad Myers avatar
Chad Myers says:
Tuesday, April 29, 2008

This is great stuff, Rob. Keep up the good work. Thanks for taking critical feedback and incorporating it into the next parts. The conversational style of multi-part screen casts is a very interesting twist on the traditional 'training' style screen casts.

One piece of advice: I don't know who this John guy is, but you've really got to get rid of him. His head is too shiny!


Stephen avatar
Stephen says:
Tuesday, April 29, 2008

Hi Rob,

The lazy list is a nice idea, but it seems to me that you wouldn't want to of changed the implementation to iqueryable anyway because iqueryable doesn't facilitate writes..

Also, your lazy list really just acts as a proxy to the underlying data model (as in, your app model class is really just carrying an object from the data model), and while I agree with this for the sake of lazy loading, this isn't exactly a pure cut between the app and data model.. it makes me wonder even more 'why here?'..

Also, something you didn't cover was how you write data back to the data model from the app model classes, how do you handle relations etc..

I'm going to download download your latest code drop to see if it sheds some light!


West avatar
West says:
Tuesday, April 29, 2008

Hi Rob,

Thanks for this series of web casts really enjoying them to see how others would go about different situations.

Any news on when the silverlight player service will be back up for the different episodes?

Keep up the good work.

Cheers


Francois Tanguay avatar
Francois Tanguay says:
Tuesday, April 29, 2008

Hi Rob,

Looks like we have the same concerns at the same time. I've been using the IQueryable to replace the query part of my repositories for a while now and it works like a charm.

The thing I disagree on is your approach on facading the entities that come out of your repository with an extra "model layer". I understand the "purist" and DDD approach but wonder how much code will be duplicated when comes time to change tracking, relationships, versioning, concurrency...

Basically, I'm saying that ORM already offers a layer of abstraction and that the model over it is one too many.

Also, it would be great if you could come up with a little stack-ranked backlog. Would get you more in the Agile mindset, clarify where you're heading and keep us waiting for more.

Good job!


Jamie avatar
Jamie says:
Tuesday, April 29, 2008

Just saw the explanation of Classic Repository versus "Repository+" and I, for one, like the way you're doing it. I'm anxious to see how the Create, Update, and Delete work using that pattern, but so far it looks really good. For many projects such as reporting apps, Filtering and Querying really are first-class concerns of the domain and this helps.

Also, regarding "too much abstraction" with having model classes on top of the Linq-generated classes... I have to disagree. Problem with LinqToSql is that it requires your classes to almost exactly map to your tables - which can cause quite a stinky model. Maybe if you want to use change tracking, relationships, etc. LinqToEntities would make more sense (if it's ready) or even LinqToNHibernate - which Ayende seems to have damn near finished from what I've played with.

As it is with LinqToSql, IMO, if you just use the generated stuff you might as well go all the way with putting your business logic in them via partial classes and collapse the layers altogether.


Stephen avatar
Stephen says:
Tuesday, April 29, 2008

Jamie, just because the linq to sql classes don't look exactly like how you want them, doesn't mean their useless.. you could interface them and only work with the interface, the interface implementation would handle the differences then.. you can also create a proxy between them..

The difference between the proxy compared to this abstraction is that, so far, the direction of mapping is just one way.. if I changed the property on the proxy, it would change the real property of the data model (ready for flush).. this model only changes the property on the app model part, meaning there has to be another set of explicit methods created to handle writing data BACK to the data model..

To me, this is the part I think will start to get messy..


Francois Tanguay avatar
Francois Tanguay says:
Tuesday, April 29, 2008

@Jamie,

Right, LINQ to SQL isn't appropriate. But if you think of nHibernate, ActiveRecord, EF, they all offer the required abstraction.


David Alpert avatar
David Alpert says:
Tuesday, April 29, 2008

@stephen - the lazy list is not simply a proxy to your data layer if it goes through IQueryable and the Repository maps it into your model as part of a deferred query. Rather, it is a strongly-typed mapping that lives in your code. Changing it requires recompiling, but if you isolate it inside a satellite assembly and wire it up with a Container, then i like the benefit of strong-typing (from both sides) and testability when building and managing that mapping.

(Phil Haack has a related post about <a href="http://haacked.com/archive/2008/04/18/dynamic-language-dsl-vs-xml-configuration.aspx">Code vs Configuration</a>)

I tried interfacing my Linq2Sql entity classes and still felt that was my data model imposing it's internal back-end structure (and property names) right up into the front-end UI and that didn't feel like any separation at all.

And this leads to...

@Francois - what i like about the facading is precisely that it cleanly separates the business model from the data model, while IQueryable and the Repository help the bits flow between them pretty easily. The Database refactoring that Rob demonstrated in the last episode points to some powerful opportunities to clean up or reorganize the data without mucking with the model. And as the model's behavior is all under test.... :-)

The only potential drawback that i see here concerns the left-hand-right-hand code that you have to write and maintain in your repository. But with strong-typing and intellisense, how much of a burden is this?


Rob Conery avatar
Rob Conery says:
Tuesday, April 29, 2008

@Stephen:

>>>this isn't exactly a pure cut between the app and data model.. it makes me wonder even more 'why here?'..<<<

I don't think I follow your reasoning on this. Can you add some detail?

>>>Also, something you didn't cover was how you write data back to the data model from the app model classes, how do you handle relations etc..<<<

Haven't got there yet :). Perhaps that's the bomb that will explode this idea :).

@West: No, I don't know why it won't process my vids. I have the SL Live team looking into it.

@Francois:

>>>I understand the "purist" and DDD approach but wonder how much code will be duplicated when comes time to change tracking, relationships, versioning, concurrency...<<<

The one thing that leaps to mind RE concurrency is inventory. There's no spec for that yet, but there will be and I know it's going to be it's own cast :). I'm going to throw Timestamps in for versioning, and relationships?

I do know that many ORM's have this (including LinqToSql). What do you mean by "stack-ranked backlog"?

@Stephen:

>>>just because the linq to sql classes don't look exactly like how you want them, doesn't mean their useless..<<<

This is actually a lot bigger point than you're allowing :). It's not a matter of "looks", it's a matter of OO vs RDBMS :). Sure it's not "useless", but making it "useful" is a bit of a challenge.

Finally - the point I keep trying to get accross, is that I need the app to be DB-agnostic. LinqToSql in my model doesn't allow that.


Francois Tanguay avatar
Francois Tanguay says:
Tuesday, April 29, 2008

@David:

I understand the idea behind facading the data model into a higher level model. However, many business rules will imply that you know the state of your domain object (is it new? existing but modified?).

Moreover, EF (to take just one) automatically generates metadata on properties as validation for range and allowed values. You can easily leverage those to drive the UI. If you wrap your "data model" into a "domain model", then you need to replicate those as well.

My average systems will have 50-100 domain entities. It starts to be a lot of efforts without many pros. To take the the data model refactoring, it is probably not totally true that it wouldn't bubble up into the higher model. Culture is something you'll want to provide in a list somewhere to choose from.

Using EF and nHibernate/ActiveRecord, I never had to duplicate those efforts. And as this project is a tentative at some form of guidance, I'm thinking we might not be using the most appropriate tool for the job.

@Rob

I just would like to see a list of requirements (like short user stories or use case names):

- The user can add a new product

- The user can search products by category

- Top 25 available products will be refreshed every 5 minutes

- ...

Basically, it's what you want to deliver in the next iteration. The business value.


Stephen avatar
Stephen says:
Tuesday, April 29, 2008

My point about the cut was that my idea was to fake the data model up into the application model.. it would allow you to mock a data model without any issues..

You stated that you wanted a seperation, I wasn't exactly sure what level of seperation you really wanted.. but since you are storing the enumerator in the lazy list, part of the data model is now inside the app model, just proxied.. (the app model calls the enumerator which in turn does its thing in the data model, of course the app model doesn't realize this).

I just found it odd that you commented against my idea saying you wanted a total seperation between the data and app model.. I started to wonder how you would get any performance out of a pure cut (which to me suggests giving the app model EVERYTHING it would need at the translation layer).

And I'm aware the look isn't the only thing with linq, infact- both our concepts are both involving proxying the data model at some point.. mine was just much lower down, and done by interfacing the data model directly..

It seemed to me that with interfaces / abstracts, you had the choice to use a proxy class, or if you had control- and a relatively 1 : 1 map of your data and app model - a direct effect on the data models inheritance.


Dietrich avatar
Dietrich says:
Wednesday, April 30, 2008

Rob this is already a great series. I hope you do more with designers – I think one of MVC’s great strengths is how easy it is to work with designers.

But so far you seem to be making the classic developer’s mistake: you’re writing code without figuring out what the client wants. Or if we’re to assume you do know what the client wants I’m hearing this:

“You never know what I might do, so create a lot of data abstraction with cutting edge technology that’s unproven and really slick.” Unless your client is MS I guarantee they didn’t say that.


Rob Conery avatar
Rob Conery says:
Wednesday, April 30, 2008

LOL Dietrich ... What! "Classic developer's mistake?" - how bout a little bit of slack here amigo...

I covered "what the client wants" in terms of the CSK - there are over 2 years of Open Source goodness going into this - things I've learned the hard way, and things I wish I could have done better.

I don't know what you're hearing, but what you just suggested is offensive, and way out of line.


Roger avatar
Roger says:
Wednesday, April 30, 2008

Before being a newbie there's the embryo stage, that's me. I stumbled on your series because I'm interested in ASP.NET MVC, coming from a classic ASP background and having never liked web forms.

Even so, you've caught my interest with your further abstraction of the DAL, if I can put it that way. It seems like you are aiming to completely separate the application from the database, so that your application could be reused regardless of, not just the database provider, but also the database diagram. By doing it this way, could your app integrate with existing data, of varying structure, by only working with your Data project, or even more simply, by just knowing what your Data project expects as validly named inputs/outputs? It strikes me as ideal for situations where you are integrating into a legacy enterprise database or a hobbled together ERP solution, where the database diagram may be dramatically different than your initially envisioned diagram or maybe not a database at all.

Am I way off here?


IrishSamurai avatar
IrishSamurai says:
Wednesday, April 30, 2008

@Rob: I wouldn't take Dietrich's comment as offensive, more likely misinformed or naive to this series and/or your previous contributions to DashCommerce ...

BTW, great series ... thoroughly enjoy your presentation style and the knowledge/ideas you are dispensing.


Jamie avatar
Jamie says:
Wednesday, April 30, 2008

@Roger - For someone who's in the embryo stage, you seem to have pinpointed exactly what Rob's trying to do. At least what it appears to me he's trying to do - though maybe I'm an embryo too and just don't get it...

If you have the time, take a look at NHibernate. It also shields your app from both the database engine and structure, but is still limited to just relational databases. I think the potential strength of Linq (especially abstracted the way Rob is doing it) is that you can expand the concept to use XML files, LDAP servers, or whatever datasource you can write an IQueryable implementation for!

I am still very anxious to see the Create/Update/Delete side of the story though. Hopefully this pattern doesn't blow up at that point.


manu avatar
manu says:
Wednesday, April 30, 2008

Hi Rob,

compliment for the webcast. It's very interesting and I like your approach.

I'm waiting for the implementation of IDisposable pattern of data context :)


Erik avatar
Erik says:
Wednesday, April 30, 2008

Good Stuff Rob. I am really enjoying this series. Forgive my ignorance, I guess I really don't understand what your lazy list gives that an IEnumerable<Product> does not. Both would give lazy loading right?

What would you give up with a

public IEnumerable<Product> Products { get; set; }

instead of a

public IList<Product> Products { get; set; }

I ask this because in playing around I am trying to figure out what you would have to add to your IList ipmplementations to allow a filter like the following

public static IQueryable<Category> WithProductID(this IQueryable<Category> qry,

int ID)

{

return from c in qry

where c.Products.Any(p=>p.ID==ID)

select c;

}

With Ienumerable I could do this but witht the Ilist(LazyList) I can not. I will spend some more time grokking when I have a chance. Another Embryo just playing around trying to get his head around this.

Erik


Rob Conery avatar
Rob Conery says:
Wednesday, April 30, 2008

@Roger - yes that's precisely what I'm going for. The limiter right now is working with IQueryable, but hopefully that will change soon. The best part is that "jacking" the datasource in will be a matter of creating 1/10 the methods you'd normally have to with the classic Repository pattern.

@IrishSamurai - informed or not, them's fightin words! :)

@Erik - good thought there and yes, I did think of it. The problem is that IEnumerable is a little too basic for what I want to do. That said - I like the approach you're using here, though I will say I don't have a requirement for it yet :).


Stephen avatar
Stephen says:
Wednesday, April 30, 2008

Surely theres a big difference between ienumerable and ilist for the model, enumerable only provides a readable system..

Of course this just changes where the control is, if the ilist handles adds and removes vs the object that exposes the ienumerable..

ie..

GetProducts()

AddProduct(..)

RemoveProduct(..)

Products

.Add(..)

.Remove(..)

.GetEnumerator()


Roger avatar
Roger says:
Wednesday, April 30, 2008

Rob, if I understand correctly, you have been involved with DashCommerce, or maybe even started the project. If so, are you aiming to achieve the same kind of functionality with this project, hopefully with some of the advantages of MVC?

Also, as the .NET framework has advanced quite a lot since the DashCommerce project began, do you have a rough idea of the development time savings you could expect by using the newer .NET features like Linq and MVC? I’m just interested in how much developer productivity may have increased as the .NET framework has matured.

Thanks…


Rob Conery avatar
Rob Conery says:
Thursday, May 01, 2008

@Roger - dashCommerce started as the Commerce Starter Kit, and I was the creator :). I haven't been involved for almost a year now and I'm sure Chris has added a lot of stuff.

In terms of dev time-savings, sort of hard to compare really since I'm using new patterns and a technology that's still being created - so I can't answer that i'm afraid.


firefly avatar
firefly says:
Thursday, May 01, 2008

Rob, keep up the good work man, this is a great series.

Not to jinx you or anything but I am actually holding my breath to see when your design is going to blow :) Because that's when we really get to see how agile and effective this approach is. Especially on the TDD end I wonder how those test case will stack up when the design requirement change. All and all if you succeed the on the first design that's great but if you don't it's even better IMHO.


martin avatar
martin says:
Thursday, May 01, 2008

Rob

There are something i don't understand or with i disagree

Sometimes, model object is fully managed by the data repository, sometimes it is fully managed by the service

Example is sqldatarepository.GetCategories() function that is in charge to return categories with products propriety setted to the lazylist<product>

whereas catalogservice.getproduct() sets itself Reviews or Images

proprieties; I see that there is added work on reviews (filter applicatio, etc) but i see it an inelegant implementation.

In fact, the user of libraries (data or/adn service) see 2 differents fashion so he has to see the code to know how it is managed.


Rob Conery avatar
Rob Conery says:
Thursday, May 01, 2008

@firefly: thanks?

@martin - good points :). I'll clean that up ...


Jamie avatar
Jamie says:
Thursday, May 01, 2008

Has your client called repeatedly and complained because you didn't have is UI demo done by Wednesday? :P


mexicanNinha avatar
mexicanNinha says:
Thursday, May 01, 2008

What's up w the irishSamurai's identicon?


Christian Schiffer avatar
Christian Schiffer says:
Friday, May 02, 2008

Hi great series Rob, I am using some of it in a new mvc site I am building.

I also like the LazyList a lot but made a change to the clear method, its just a detail but I am thinking that we don't need to clear the list if its null; hence not invoke the call to the datastore... I might be tiered now so I might be wrong, but this is what I did:

public void Clear()

{

if (_inner == null)

return;

Inner.Clear();

}


Jimit avatar
Jimit says:
Monday, May 05, 2008

@Christian Schiffer: Good point Christian. To add to that, you might want to clear the query or something to prevent the inner list from being repopulated when the enumerator is called. Otherwise even though you'd cleared the list, it would still get repopulated.

@Rob: I'm with a couple of the others in kinda holding my breath waiting for the pattern to fall flat on its face. Don't get me wrong, I think it's fantastic using IQueryable's versatility in the way you have, but while the pattern works great for pulls, pushing data back to the database's gonna be a bit tricky. You may already have some lil' bit of magic in reserve to address this specific problem but I can't wait to see how it'll turn out. I'm using the some of the data access strategies discussed so far in a project of my own at the moment. I especially like the idea of keeping around a readonly datacontext with object tracking disabled for queries. That should work great for lazy loading especially since you never know when the query will actually be executed.

I agree with some of the points made above about keeping the mapping logic between your model classes and the linq2sql classes in the repository rather than dividing it between the service and the repository. I think the service's sole task should be to query the repository and not worry about mapping details. This might also be made easier if you kept each repository within strictly defined aggregate boundaries and only returning entire aggregates from the repository - true to more mainline DDD.


Jelle Hissink avatar
Jelle Hissink says:
Tuesday, May 13, 2008

Rob,

why do you let the constructor of LazyList<T> take an IQueryable<T> instead of IEnumerable<T> you only use functionality provided by methods from the IEnumerable<T> right?

Keep up the good work!

Jelle



Search Me
Subscribe

Popular Posts
 
My Tweets
  • @codinghorror: I may be weird but I don't ask my cats to write my blog entries :p.
  • @blowdart sure! Wanna come on and do a webcast with me - plugging CardSpace in?
  • People are very, very weird. Skypecasts are creepy.
  • Is Rob Howard trying to tell us something? http://www.rob-howard.net/
  • New storefront posted- all about OpenID :) http://tinyurl.com/5rkgux
  About Me



Hi! My name is Rob Conery and I work at Microsoft on the ASP.NET team. I am the Creator of SubSonic and was the Chief Architect of the Commerce Starter Kit (a free, Open Source eCommerce platform for .NET)

I live in Kauai, HI with my family, and when my clients aren't looking, I sometimes write things on my blog (giving away secrets of incalculable value).