Friday, December 05, 2008 -
I don't like the complexity that's crept into my application. Not sure how else to put it - but the architecture needs to be shaken a bit so in this episode I tackle this complexity head on using DDD.
The Zen of Smell
You can't really define it when your app starts to smell - and I really hate that term! But it really applies to what I'm encountering right now as I begin to not only wrap up what I'm doing, but also demo it. People have questions, things aren't apparent.
I have answers to many of the architectural decisions but when I explain how they are carried out, I realize that I've just run over some potholes.
In short: I could have done better.
Continuous Learning
That's what this is really all about: we never stop learning and there's always some refinement to be made. Yes I realize this can be a vicious cycle of never-delivery, but I'm also confident that the pursuit is always worth it. In this case, I'm relying on my 20 years of coding and development to elbow me in the ribs and suggest to me that "I can do better".
I can make this application better, faster, stronger than before.
I don't want to give in just yet and thankfully my new boss is a rockin super star and is letting me further explore this (seriously - send this man some love for letting me experiment).
There is gold in these hills. The journey I've set out on has already transformed the way I do things forever and all I hope and pray is that I am able to inject this goodness into the Storefront.
Your Patience, As Always, Is Appreciated
To me, the payoff of this entire effort has been the journey itself. .NET has matured to major rev 3 and with it we have a great toolset (Linq, Anonymous types, MVC, etc). It's sort of like wandering around [Killer Video Game], realizing you've leveled up and have unlocked The Next Great Zone.
Exploration is in order, the testing of new powers, the downing of bigger bosses, etc.
I can see the finish line - feature-wise there is no more to do. Right now it's all about honing and refactoring so sit back and watch Part 1 of a 3-part process, as I take on the "drift" in the Storefront and try to apply some Zen to the madness.
The Source
I'm working very hard to synch up everything I'm working on now, and it changes (literally) hourly. I just ask your patience as I get things put together and structured properly. This is not a quick affair, but something that takes time and discipline and I don't want to short-change it. I really think it will be worth it.
Download Episode 25 It Here (42 Minutes, 62Mb)
Double-click for full screen
It is hard to wait when you have no estimated time, though.
anything until I'm finished :). Too many spent cycles writing docs that get
outdated the next day.
I have a whole episode I want to do RE "Being a good Dev - Let's Analyze and
Document" etc.
I'm hard at work on this stuff and am working on something pretty dang cool
at the moment :)
Im just after a practical approach to the documentation that gives a written document not a video. As useful as your are in explaining and bringing together a sensible approach to the technology at our finger tips, I would like to see you apply that same practical common sense to a documentation effort.
have to). Generally people aren't very interested in watching me write docs
- they want to see the code and learn how to do things; thus the videos.
WTB ASP.NET MVC RC! (that's a lot of acronyms)
/me drools at how close we are learning from this project once more!
a holiday where you live, make one, then do something good for it! Festivus!
In other example I've seen, all entities inherit from an EntityBase which implements IEntity, and all aggregate roots specifically implement an IAggregateRoot to specifically differentiate themselves from other entities. Do you see any value in doing that?
Very well delivered, looking forward to the next part!
completely free of this kind of thing (according to the books) - they call
it "POCO as a lifestyle" :).
I think I know the example you're referring to and the only thing IEntity
does is make you declare a key (TKey). The only thing that does (in that
example) is allow the developer to build out a SQL string.
Inheritance on your objects is coupling - even if it's helping you out (my
opinion). But again - I'm no expert here :).
you're right. Generally not. It doesn't mean that we shouldn't always try
for something better :)
so this is what i came up with..
Unit Of Work.. Currently you're implementing the ISession.. this only works for SQL based items..
At the end of the pipeline i'm assuming there will be some sort of email being generated..
Should this email be using Unit Of Work ?? how are you going to handle non sql work items??
Are these simply ignored because there might be only one email generated?
- not sure. I'll ask a wonk...
// User with attributes related to security context
class Security.User : UserBase {
RoleCollection Roles { get; set; }
EmailAddress Email { get; set; }
}
// User with attributes related to the ordering context
class Orders.User : UserBase {
OrderCollection PastOrders { get; set; }
}
// Base of users across contexts, a Security User and a Orders User could be the same, how? By identity.
abstract class UserBase : EntityBase
}
// Provides what it means to be an entity, it must have an Identity
// Equals, equality operators, and HashCode have to be based on type, super type, and Identity, not other attributes and pointers
abstract class EntityBase
TIdentity Identity { get; }
Equals(object obj) { ... }
operator ==(left, right)
operator !=(left, right)
GetHashCode() { ... }
}
// generic interface
interface IEntity
TIdentity Identity { get; }
}
// marker interface, can be useful for constraints elsewhere...
interface IEntity {
}
I personally don't see how this is not POCO. I'm avoiding duplication and I haven't implemented anything outside of my control for the sake of satisfying my persistence layer. What I'm satisfying is the definition of what it means to be an entity, it has an identity and that equality should be based on identity... You could still provide complex TIdentity types if you wanted something more than primitive ints, Guids, etc.
Of course, you could implement equality and identity on each and every one of your domain entities if you choose, but why not bubble it up to a super type?
domain model, and to whom? I'm not disagreeing with you necessarily - I
think it's just not clear to my WHY I need to do what you're suggesting. To
be upfront - it comes off as "Hyper coding". Please don't take offense :) I
really appreciate your thoughts but supporting your points with DDD
terminology isn't making sense to me (sounds jargon-y). In other words:
After having a bit of a think about it.. the email seems like a side effect.
But you get the jist.
One of the samples I've seen uses this technique, but then they also embed SQL calls and use the Key. Knowledge of the key is something I give the domain - it doesn't give it to me (that's the way I see it at least). It seems backwards to me, but I do know that a lot of people do this.
In terms of learning - me too :). But I'm trying to be pragmatic as well and this feels like silliness :)
The part about services just calling the repositories. That seems necessary to me, the GUI layer should not directly call the data access layer, right? This was only mentioned, but there was no solution in this screencast.
Another thing I don't get: does bounded context mean that we are going to have multiple Product classes because in a different context it needs to have different properties? It seems to me that at this point you also are talking about two completely separate applications?
Also, this is a huge step for a refactoring. I understood the previous version of the solution pretty well, and it feels like _everything_ was moved around. It kind of puts the previous 24 episodes in a whole new light. It's like you are saying: you know the past weeks all the stuff I showed you, don't do it that way, do it this way. That's fine, IF you are still watching at this point. I don't want to sound to negative, because I still really appreciate this series, but I hope you are not going to tell us in episode 52 that actually, it would be better if... you know what I mean?
Thanks! Looking forward to the next episode.
var user = Repository.Get
foreach(var order in user.Orders) {
// user has an OrderCollection called Orders
}
// contrived, elsewhere you do this for some reason
var order = Repository.Get
if(user.Orders.Contains(order)) {
// do whatever
}
Assuming that the User's OrderCollection does in fact contain Order #42, the only way this works is via an appropriate definition of equality, otherwise we're relying on object reference equality.
The question then is what is appropriate equality, this should come from the domain, perhaps it's a unique set of attributes on the object, but it often tends to be a surrogate. I think Evans somewhere talks about a person, what identifies a person that's the same for their entire life (and even after they're dead), his answer is nothing, but there's still something that uniquely identifies a person. To me that's something meta, so to me, identities/keys tend to be surrogates.
So is five year old Rob Conery the same as (insert Rob's age now) Rob Conery? Sure. So if I do that in code, youngRob.Equals(currentRob) I should get true, so equality must be implemented around this identity.
Given that we want identity equality, the reasonable thing would seem to be to define entities as having an identity and have a base class implement the equals, gethashcode, and equality operators to avoid duplication. Now if you want a non-surrogate identity, you could implement your own identity type and have it to equality however you want, still the base class delegates to the TIdentity for equality after the normal checks for null and reference and type equality.
This seems completely reasonable to me, hopefully this helps clarify it?
Thanks Randy - appreciate the response. It got me thinking about Aggregate Roots and if I understand correctly, User (in your example) is an Agg root for order. Therefore your second call to the Repository is sort of violating something, no?
As part of my domain, currently, I recognize SKU as the identity of Product (which is what my client does), and OrderNumber is the Identity of the Order (which my client does). I then override Equals as appropriate for each. I don't need to tag SKU as the identity - I know it since that's the design.
I think you're point might be "what about other devs" and that's valid. The question then becomes do I need to implement an interface so other devs know what my Entity key is? I think that's too much (personally) - I'd rather use a comment.
But... I'm still listening...
RE Bounded Context - hopefully not - that would be confusing :). I'll do my best to make it clear.
>>> It kind of puts the previous 24 episodes in a whole new light. It's like you are saying: you know the past weeks all the stuff I showed you, don't do it that way, do it this way
The previous episodes worked fine for the shape of the app then - it followed some pretty standard way s of doing things. However as I've been "buttoning up", I've realized I could do better.
I never said it was "wrong" - I said I could "do better". Smells don't mean wrong - they're indicators that refactoring is needed. In this case, I decided to try to work with something that was in the back of my mind, DDD.
I understand it seems like an abrupt course change - it's not really. I'm simply moving a few things around and adding some clarity. When I'm done, hopefully you'll see that. File ops don't necessarily equal Dynamite - all of the main code bits are still the same, and the web site is untouched at this point.
interesting video. It does make me see this all in a new light. I need to go over this a few times to really get my head around it .. but i love the angle of attack you're using. If it works .. great! if not .. we still learn from this. It's a positive direction .. good work!
I LOVE the IQueriable magic - that has solved so many probs for me. Great to see you're a fan of that (still). Adding the ISession is also something that i'm sooo gonna do now - great stuff! I used to have two methods in my Service project (Create and Update), which ended up calling a single Save method on the repository. I can now clean this up even further with the UnitOfWork pattern.
A few questions: (which i'm guessing will be answered in #26 and #27)
*) What happened to 'pipelines' ? I'm now using WF because of what i learnt from your vids. Does this still have a place? I'm guessing there will be seperate workflows per project, as these workflows will be encapsulated within the domain that they are relevant for (eg. order pipelines found inside the OrderProcessing project; sales pipelines found inside the Sales project.. etc).
b) why did u call it ISession and not IUnitOfWork ? (i'm not techincally familiar with the UoW pattern, even though i understand how i've been using it in Linq2Sql).
Hopefully we don't need to wait as long for #26 and #27 (with all due respect). I'm also curious to see how some POCO items will be handled across a number of projects to minimize duplication but to avoid polluting the Infrastructure project.
Lastly, is it worth adding an extra line on the first slide -> Storefront discussion forums: http://forums.asp.net/1165.aspx ?? :)
I don't mean to beat a dead horse, but I totally disagree with Dave Laribee defending that DDD is a set of patterns. Sure, there are lots of patterns like repository, unitofwork, factory, specification, etc... but they are just a means to an end which is to write code that captures the essence of the business. DDD is a perspective or philosophy even, not a set of techniques.
I liked Scott Gu's use of the term "impedance mismatch" applied to the rift that exists between the business concepts and the software concepts. Business people think in their language and when they come up with a business idea, it is an extension of their language. So, if your code reads like their business language, the new features are a snap because they extend naturally from your design.
I'm looking forward to the next two episodes! FYI listen to yourself on hanselminutes, you sound just as vague and heady as the rest of us describing DDD! Ha!!!
thinking about it all day (honestly) and what I think it comes down to is
I'd rather rely on overriding Equals (since it ripples into Contains() and
other collection stuff) than an explicit key definition.
I do think it's worth thinking this part through - I do agree that
programmatically knowing when something is an Agg Root (vs. Entity vs. Value
Object) may be worthwhile. I'm also wondering how that would play out. It
would seem that if you need to ask your model "hey what is this thing" than
maybe you're doing something wrong?
This is good stuff here - and yes I think we're close on this. I'm trying to
figure it out :)
ps- i'm on cable modem broadband and never had an issue downloading anything before.
ByIdentity(string identity) / ByIdentities(string[] identities)
Are both made possible by a contraint to IAggregateRoot on the repository.
I'm very much learning too, but this design seems pretty natural to me. But so did datasets in 2002 ;)
Define "set of patterns" how you will. At a certain point we get into a semantic argument unless we agree to call a pattern anything that's repeatable that we've noticed is good. DDD, the book/approach/philosophy/whatever, is a set of patterns in this light.
All that said, it doesn't really matter how we cut it. The main point is that we can take a number of things and apply them to this project or that. In doing so we should be sensible and not necessarily dogmatic or doctrinaire about our approach. What works for this won't work for that, etc.
The key thing is you protect your domain but you can do DDD and have your GUI directly talking to repositories and domain objects. This came up in a recent discussion on the DDD forum:
http://tech.groups.yahoo.com/group/domaindrivendesign/message/9028
Perhaps not the usual case but it can work as long as you make sure your GUI doesn't unduly influence your domain model.
"Another thing I don't get: does bounded context mean that we are going to have multiple Product classes because in a different context it needs to have different properties? It seems to me that at this point you also are talking about two completely separate applications?"
See the book but yes potentially it does. Models make sense in a particular context so to give you a simple example a Customer in a Finance context might be different to a Customer in a CRM context so having seperate representations is valid.
If it's a value object it behaves like a value object and if its an aggregate root then likewise. However enforcing constraints, such as that only aggregate roots have repositories, can be useful which is why the interfaces/base classes might help. Having an IDomainEntity interface and (optional) base class is, to me, not a major issue at all.
I'd suggest you mine the DDD newsgroup, its full of discussion on all these sorts of topics.
I'd thus recommend that before trying DDD you really think about how far they want to go with it. If the problem is simple and not suited to DDD then being too rigorous with it can be silly, having said that I still think some of the patterns like aggregate/repository/service/entity/value object can apply even if the overall project does not suit DDD.
for your patience. I'll implement!
About Oxite
http://visitmix.com/Lab/Oxite
Source
http://www.codeplex.com/oxite
also of note is
oomph - a microformat toolkit (hCard, hCalendar)
http://visitmix.com/Lab/oomph
Wouldn't it be better for Rob to use DDD and fail to teach the community something anyway?
This project is about learning and sharing. If you have a project that is similar in size then it's your choice to use it or not.
I'm sick of hearing this project isn't big enough non sense, Rob has gone to too much effort to learn this stuff..
Why don't we all go back to the dark ages and argue about which Text Editor is the best?!?!?!?!
I'm not having a go at you Colin just everyone in general shooting down the idea.. what's the worst that could happen really?
These are all great ideas and I can't wait to look over the code. I'm still hazy on exactly the separation of where a repository stops or starts. In my current project I am dealing with shipments. On the database side there are 5-6 different tables each with their own class / mapping in NHibernate. On the other hand, I don't want the shipment to care. I want to create it, ask the user for some information, and then process it.
So this leaves me with two questions I need to examine (in either your code or future screencasts -- this was actually my first one although I've read your blog awhile!):
1) Is the repository responsible for concatenating these 5-6 tables and spitting out a 'shipment' object, or does the repository spit out the 5-6 tables and then a different layer groups them and spits out a 'shipment' object.
2) The 'shipment' class is going to have some values to show the user, some empty values to ask the user, and some values that simply get translated / worked on behind the scenes. Figuring out how to deal with the UI layer is troubling. Do I have another object that the 'shipment' class gets translated to just for the UI? That would mean a shipment exists in 3 sort of contexts -- what a user sees, what the application sees, and what the database sees. Maybe this is right, or maybe the application and user should be the same?
I like your stuff very much. It's been an inspiration for me during my learning process of ASP.NET MVC and DDD.
hmm ... well, J thought you might be interested in this post when I read it via a link in the ALT.NEt Yahoo mail group:
http://the-software-simpleton.blogspot.com/2008/12/twat-of-ddd-increasing-complexity-in.html
The amount of effort Rob has put in has nothing to do with whether it's a project that DDD is suited to. I'm also not sure that the fact that you are "sick of" hearing something is necessarily a compelling argument. Also note I didn't say it wasn't "big enough", I've worked on a big but relatively simple project and although we did use some DDD patterns it certainly didn't just the full DDD approach.
Back to the original point, my main point was that Eric Evans has indicated that DDD is not for every project. If you think he is incorrect then that's perfectly valid of course.
> Why don't we all go back to the dark ages and argue about which Text Editor is the best?!?!?!?!
Not even sure what this means, I dunno when these dark ages were and why people were arguing about text editors but it doesn't seem related to my point. Also all the "?!?!?!?!" is totally un-necessary and a little childish.
> I'm not having a go at you Colin
I sense this is not true?!?!?!?!?! :)
dataContext.GetTable
dataContext.oxite_MessageToAnonymous.InsertOnSubmit (messageTo.MessageToAnonymous);
Keep on going on, you are on the right track! I have been using OO development for a while and DDD helps a lot bringing the concepts to reality and managing complexity.
I have to agree with Colin, Damien and others on the use of an EntityBase super class and also an IAgregateRoot interface You will get its benefits when you implement your repository.
Its like meditation; don't listen to the noisy ones. :-)
I really admire your dedication and enthusiasm.
Santos
So, are TDD and DDD competing methodologies? Maybe (I don't want to start a religious war - this is based on my experience using both), but I have found that they can both be leveraged to create a form of agility that will help you in reaching for a 'supple' design. Start by designing the domain model first using DDD, then use TDD to validate the design. Before writing a single line of 'implementation' code, make sure the tests feel smooth. Why does this help, it helps to see the big picture by using DDD and having that conversation with your customer. By utilizing TDD, you validate your model will work without too many difficulties. TDD will expose areas that don't feel 'clean' when implementing your scenarios, which is an indicator to go back to the design and clean it up. You may want to iterate over each bounded context, build the Sales model first, validate it, then move on to OrderProcessing. (BTW, TDD for code coverage is a flawed methodology, but that's another religious war I don't want to get involved with)
Next, regarding your OrderState implementation. Is it reasonable to say that an order state is responsible for refunding an order? I get the "smell" feeling you mentioned, which probably means no. The process of refunding an order is not the responsibility of the order state in this context.
To contrast, think of a workflow and it's state. The WorkflowState can keep track of things like: can the workflow start, is it started, is it completed, etc... But should that state be responsible for starting the workflow? Let's take a swag at the code to start a workflow: workflow.State.Start() vs. workflow.Start(). So, just to illustrate this a little more on how the state pattern does helps the workflow object, lets take a look at the framing of a potention workflow.Start() method.
void Start() {
if (!this.State.IsStartable)
throw new InvalidOperationException();
this.State = new StartedWorkflowState(this);
try {
// ... execute the workflow
this.State = new CompletedWorkflowState(this);
} catch (Exception ex) {
this.State = new TerminatedWorkflowState(this, ex);
}
}
I sincerely hope this helps you and keep up the fabulous work, it is quite inspiring.
/J
Rob, nice article! I wish for these concepts to become broadly adopted. I've been having a hard time promoting DDD amongst my peers... For example, it was just announced that our "business dll" would be named "UBECL" for Unified Back End Common Library...
Personally I was rooting to call it "SAURON" .. one DLL to support 10 applications, one DLL to bring them all, and in the darkness bind them ;)
BTW they're the same because oxite_MessageToAnonymous is a property that just calls GetTable
Sent from my phone. Please excuse brief replies.
The problem you are dealing with just screams for the strategy pattern and actually, it would be quite easy to refactor the code that you already have. You could keep an abstract class (it might as well be an enum) to determine the state of the order but move all the logic to separate objects implementing an interface like IProcessingStrategy or inheriting from an abstract class ProcessingStrategy, which would have all the methods that your current OrderState class holds. You would also have to add public methods like Refund, Return, etc. to the Order class. All you have to do now is create a factory that will provide the necessary strategies when needed and avoid the necessity to make any changes to your Order class in the future. Here's a bunch of code that will probably better illustrate the pattern than the above text:
public enum OrderState {
State1,
State2
}
public class Order
{
public OrderState CurrentState { get; set; }
public void Charge() {
ProcessingStrategyFactory.GetProcessingStrategy(this).Charge();
}
public void Close() {
ProcessingStrategyFactory.GetProcessingStrategy(this).Close();
}
}
public static class ProcessingStrategyFactory {
// probably the only thing you need to modify if you decide to add new strategies
public static ProcessingStrategy GetProcessingStrategy(Order order) {
switch(order.CurrentState) {
case OrderState.State1:
return new FirstProcessingStrategy(order);
case OrderState.State2:
return new SecondProcessingStrategy(order);
default:
return null;
}
}
}
public abstract class ProcessingStrategy {
protected Order _order;
public ProcessingStragegy(Order order) {
_order = order;
}
public abstract void Charge();
public abstract void Close();
}
public class FirstProcessingStrategy : ProcessingStrategy {
public FirstProcessingStrategy(Order order) : base(order) {}
public override void Charge() {
// some logic here
}
public override void Close() {
// some logic here
}
}
public class SecondProcessingStrategy : ProcessingStrategy {
public SecondProcessingStrategy (Order order) : base(order) {}
public override void Charge() {
// some logic here
}
public override void Close() {
// some logic here
}
}
Good job with this series. I have learned allot from your masochistic self review. It provides something I have never seen in any dot net blog before : perceptive. Something that is worth allot more than any "Coders Morality Of The Day". Let's face it, the nay Sayers just don't have the balls for it.
First, I really like the way you're heading with your DDD approach now and since you've asked for it: here's a set of Q's/R's:
- Why are you calling SubmitChanges in the repository? Unless you're using something like a TransactionScope, you're breaking your UoW, or not? You'll probably call SubmitChanges in the end anyway (I supppose).
- Why do you expose the OrderState property on Order? Wouldn't it make more sense to call methods like Submit and Close on an order itself (which than delegates it to it's order state)? (You could also implement the state pattern on the Order itself -> NewOrder, SubmittedOrder, ... with the same virtuals on Order of course)
What do you think?
Can someone explain the difference between unit of work and a transaction? I assume that in most cases the behavior would be the same and it is very easy to rap everything in a TransactionScope.
I already see evidence of well thought out architecture and well am impressed frankly.
I like your approach and your results. I am not assuming you won't do any doco.
I am raising it as something I would like to see.
I would like to see both your approach and your results to the doco side of things.
Keep up the good work.
asking you, be patient.
I think the term UoW is used far too much anyway, especially in today's world where you have things like TransactionScope and everybody trying to achieve persistence-ignorance (which is basically the opposite of what you would do with a real UoW - you don't want to call methods on your UoW to register dirty objects, for example).
1) I used StructureMap 2.5 with the exact pattern you used to create the DB context. When I used InstanceScope.Hybrid a new instance of the DB object was created on the creation of every new repository object. When I switched it to InstanceScope.HttpContext only one is being created per request. Are you seeing the same occurrence? Have you checked to make sure the context is the same for each http request or just assumed it was?
2) I really like your LazyList. So much to the point I used it in my project. I don't know if you have addressed this, but an object graph containing a LazyList shouldn't be cached as the internal IQueryable holds a reference to the context (DB) object. As long as the object is cached the context object will hang around even if ToList() has been called on IQueryable reference. From what I understand this might not be a problem with LINQ to sql and EF(sql). However, if you decided to use another ORM with a LINQ implementation the connection objects might not be disposed of properly. Since the connection has already been created and not destroyed or returned to the connection pool, I believe you will run into a problem. If I am wrong please let me know. I know you want to keep web apps as stateless as possible however if it can be done it will be done; and runtime exceptions are the hardest type to debug.
I noticed that TranslateOrder has a comment that an Order should always be in the database. There is then code to handle it not being present. Would it be better to log and throw an exception if it shouldn't happen?
In TranslateCustomer there is similar code to check if the Customer exists in the database. If they do not then a Customer is newed up. The Customer is never added to the _session however (that I can see) and so is lost. Obviously, this could be fixed with an appropriate call to _session.Insert but I still think it may be better that if you check for something that cannot happen that you flag it as an error.
In OrderProcessing.Order.cs you have declared Items as being of type LazyList
Keep up the good work, Paul
Thanks for the great series. I've just seen episode 13, but i'm posting here anyway.
You are actually helping me stay in shape, because I streaming your episodes to the big screen in the living room(xbox 360) where I watch you while working on my hometrainer (hope that didn't sound creepy :-) )
Tell your boss that this is what we want. If he stops listening tell his boss.
I'm toying around with MVC and there is a lot of <%= Html.Encode(Model.Message) %> going around. Which was not the case in asp.net (you could but you should not)
<%= actually means Response.write() so why not change it to <%= is Html.Encode(response.write())
Which you have to do about 95% of the time.
The other 5% can do <% Response.Write("not encoded stuff") %> or <% Reponse.KillerGenericExtensionMethod
That should put a lot XSS baddies out of business.
They are mainly about code smell, which Rob has identified strongly.
I wish Rob's boss can read all this and ask him to go 100% on MVC SF for a month so he can knock that baby out of the park and we can all celebrate and make awesomesauce websites.
long vacation :) so that's the main reason for the slow down. Well, that and
Oxite :).
My boss is Bertrand Le Roy (bleroy) if you want to petition him for my time
:):):)
I hope others email Bertrand so he (or the powers that be) can see how important this series is and will become. It's our cheat sheet to the taking over the net, one MVC proj at a time.
One thing I'd suggest is using an interface for your domain models/objects instead of creating a whole other concrete class, it just seems... meh. A good example of doing it this way is to take a look at the latest Kigg at http://www.codeplex.com/Kigg. The way they have developed their DAL is fantastic and even contains a UnitOfWork object.
Hope this helps, keep up the good work!
details? Also, with the latest bits I have here, I have Unitofwork built in.
I meant instead of having 2 sets of concrete objects (POCO and LINQ to SQL generated) you could have an interface. Then extend your LINQ to SQL generated objects by using their partial class-ness and than adding the I
When will we get a chance to get your latest code base? I'm really interested to see the code for your state pattern implementation.
though and say that it ties me a little too closely to Linq to Sql. In other
words my domain bits will rely on ORM-generated code. I also can't work with
"value objects" really (like Address) which exist in the DB and would have
gets/sets (even though I'm not fully embracing that now :).
Either way - good stuff. If you have a blog I'd love to see more on the idea
since overall it's a nice pattern. RE Kigg/DDD I haven't seen their code but
I did manage to work up a nice UoW thinger for what I'm doing.
I hope to have the new source up in the next few days.
http://weblogs.asp.net/chadmoran/archive/2009/01/16/using-interfaces-for-domain-entities-part-1.aspx
I know you don't want to be tired to ORM-generated code and you don't have to be if you setup some IoC and spread out your assemblies properly. I agree that you should stay loosely coupled to your DAL so that you code can be very agile. However, I think the interface contracts for domain objects has it's benefits. I'm not against using POCO objects I just think having to translate your queries between the objects can get really messy. I tried following your series step by step to better help me understand the code and I just couldn't stand it when I had multiple one-to-many and many-to-many relationships, I felt like I was writing SQL for something that already was.
Maybe it's just me but unless you have security issues I think you can still stay loosely coupled and still achieve your goal of not dependong on ORM generated code... you have to somewhere.
Hope this helps and can't wait to see your code, nice house by the way.
you're right. Generally not. It doesn't mean that we shouldn't always try
for something better :)
a holiday where you live, make one, then do something good for it! Festivus!
In other example I've seen, all entities inherit from an EntityBase which implements IEntity, and all aggregate roots specifically implement an IAggregateRoot to specifically differentiate themselves from other entities. Do you see any value in doing that?
Very well delivered, looking forward to the next part!
completely free of this kind of thing (according to the books) - they call
it "POCO as a lifestyle" :).
I think I know the example you're referring to and the only thing IEntity
does is make you declare a key (TKey). The only thing that does (in that
example) is allow the developer to build out a SQL string.
Inheritance on your objects is coupling - even if it's helping you out (my
opinion). But again - I'm no expert here :).
// User with attributes related to security context
class Security.User : UserBase {
RoleCollection Roles { get; set; }
EmailAddress Email { get; set; }
}
// User with attributes related to the ordering context
class Orders.User : UserBase {
OrderCollection PastOrders { get; set; }
}
// Base of users across contexts, a Security User and a Orders User could be the same, how? By identity.
abstract class UserBase : EntityBase<Guid> {
}
// Provides what it means to be an entity, it must have an Identity
// Equals, equality operators, and HashCode have to be based on type, super type, and Identity, not other attributes and pointers
abstract class EntityBase<TIdentity> : IEntity<Tidentity> {
TIdentity Identity { get; }
Equals(object obj) { ... }
operator ==(left, right)
operator !=(left, right)
GetHashCode() { ... }
}
// generic interface
interface IEntity<TIdentity> : IEntity {
TIdentity Identity { get; }
}
// marker interface, can be useful for constraints elsewhere...
interface IEntity {
}
I personally don't see how this is not POCO. I'm avoiding duplication and I haven't implemented anything outside of my control for the sake of satisfying my persistence layer. What I'm satisfying is the definition of what it means to be an entity, it has an identity and that equality should be based on identity... You could still provide complex TIdentity types if you wanted something more than primitive ints, Guids, etc.
Of course, you could implement equality and identity on each and every one of your domain entities if you choose, but why not bubble it up to a super type?
domain model, and to whom? I'm not disagreeing with you necessarily - I
think it's just not clear to my WHY I need to do what you're suggesting. To
be upfront - it comes off as "Hyper coding". Please don't take offense :) I
really appreciate your thoughts but supporting your points with DDD
terminology isn't making sense to me (sounds jargon-y). In other words:
One of the samples I've seen uses this technique, but then they also embed SQL calls and use the Key. Knowledge of the key is something I give the domain - it doesn't give it to me (that's the way I see it at least). It seems backwards to me, but I do know that a lot of people do this.
In terms of learning - me too :). But I'm trying to be pragmatic as well and this feels like silliness :)
var user = Repository.Get<User>(42);
foreach(var order in user.Orders) {
// user has an OrderCollection called Orders
}
// contrived, elsewhere you do this for some reason
var order = Repository.Get<Order>(42);
if(user.Orders.Contains(order)) {
// do whatever
}
Assuming that the User's OrderCollection does in fact contain Order #42, the only way this works is via an appropriate definition of equality, otherwise we're relying on object reference equality.
The question then is what is appropriate equality, this should come from the domain, perhaps it's a unique set of attributes on the object, but it often tends to be a surrogate. I think Evans somewhere talks about a person, what identifies a person that's the same for their entire life (and even after they're dead), his answer is nothing, but there's still something that uniquely identifies a person. To me that's something meta, so to me, identities/keys tend to be surrogates.
So is five year old Rob Conery the same as (insert Rob's age now) Rob Conery? Sure. So if I do that in code, youngRob.Equals(currentRob) I should get true, so equality must be implemented around this identity.
Given that we want identity equality, the reasonable thing would seem to be to define entities as having an identity and have a base class implement the equals, gethashcode, and equality operators to avoid duplication. Now if you want a non-surrogate identity, you could implement your own identity type and have it to equality however you want, still the base class delegates to the TIdentity for equality after the normal checks for null and reference and type equality.
This seems completely reasonable to me, hopefully this helps clarify it?
Thanks Randy - appreciate the response. It got me thinking about Aggregate Roots and if I understand correctly, User (in your example) is an Agg root for order. Therefore your second call to the Repository is sort of violating something, no?
As part of my domain, currently, I recognize SKU as the identity of Product (which is what my client does), and OrderNumber is the Identity of the Order (which my client does). I then override Equals as appropriate for each. I don't need to tag SKU as the identity - I know it since that's the design.
I think you're point might be "what about other devs" and that's valid. The question then becomes do I need to implement an interface so other devs know what my Entity key is? I think that's too much (personally) - I'd rather use a comment.
But... I'm still listening...
thinking about it all day (honestly) and what I think it comes down to is
I'd rather rely on overriding Equals (since it ripples into Contains() and
other collection stuff) than an explicit key definition.
I do think it's worth thinking this part through - I do agree that
programmatically knowing when something is an Agg Root (vs. Entity vs. Value
Object) may be worthwhile. I'm also wondering how that would play out. It
would seem that if you need to ask your model "hey what is this thing" than
maybe you're doing something wrong?
This is good stuff here - and yes I think we're close on this. I'm trying to
figure it out :)
If it's a value object it behaves like a value object and if its an aggregate root then likewise. However enforcing constraints, such as that only aggregate roots have repositories, can be useful which is why the interfaces/base classes might help. Having an IDomainEntity interface and (optional) base class is, to me, not a major issue at all.
I'd suggest you mine the DDD newsgroup, its full of discussion on all these sorts of topics.
for your patience. I'll implement!
ByIdentity(string identity) / ByIdentities(string[] identities)
Are both made possible by a contraint to IAggregateRoot on the repository.
I'm very much learning too, but this design seems pretty natural to me. But so did datasets in 2002 ;)
so this is what i came up with..
Unit Of Work.. Currently you're implementing the ISession.. this only works for SQL based items..
At the end of the pipeline i'm assuming there will be some sort of email being generated..
Should this email be using Unit Of Work ?? how are you going to handle non sql work items??
Are these simply ignored because there might be only one email generated?
- not sure. I'll ask a wonk...
After having a bit of a think about it.. the email seems like a side effect.
But you get the jist.
The part about services just calling the repositories. That seems necessary to me, the GUI layer should not directly call the data access layer, right? This was only mentioned, but there was no solution in this screencast.
Another thing I don't get: does bounded context mean that we are going to have multiple Product classes because in a different context it needs to have different properties? It seems to me that at this point you also are talking about two completely separate applications?
Also, this is a huge step for a refactoring. I understood the previous version of the solution pretty well, and it feels like _everything_ was moved around. It kind of puts the previous 24 episodes in a whole new light. It's like you are saying: you know the past weeks all the stuff I showed you, don't do it that way, do it this way. That's fine, IF you are still watching at this point. I don't want to sound to negative, because I still really appreciate this series, but I hope you are not going to tell us in episode 52 that actually, it would be better if... you know what I mean?
Thanks! Looking forward to the next episode.
RE Bounded Context - hopefully not - that would be confusing :). I'll do my best to make it clear.
>>> It kind of puts the previous 24 episodes in a whole new light. It's like you are saying: you know the past weeks all the stuff I showed you, don't do it that way, do it this way
The previous episodes worked fine for the shape of the app then - it followed some pretty standard way s of doing things. However as I've been "buttoning up", I've realized I could do better.
I never said it was "wrong" - I said I could "do better". Smells don't mean wrong - they're indicators that refactoring is needed. In this case, I decided to try to work with something that was in the back of my mind, DDD.
I understand it seems like an abrupt course change - it's not really. I'm simply moving a few things around and adding some clarity. When I'm done, hopefully you'll see that. File ops don't necessarily equal Dynamite - all of the main code bits are still the same, and the web site is untouched at this point.
The key thing is you protect your domain but you can do DDD and have your GUI directly talking to repositories and domain objects. This came up in a recent discussion on the DDD forum:
http://tech.groups.yahoo.com/group/domaindriven...
Perhaps not the usual case but it can work as long as you make sure your GUI doesn't unduly influence your domain model.
"Another thing I don't get: does bounded context mean that we are going to have multiple Product classes because in a different context it needs to have different properties? It seems to me that at this point you also are talking about two completely separate applications?"
See the book but yes potentially it does. Models make sense in a particular context so to give you a simple example a Customer in a Finance context might be different to a Customer in a CRM context so having seperate representations is valid.
interesting video. It does make me see this all in a new light. I need to go over this a few times to really get my head around it .. but i love the angle of attack you're using. If it works .. great! if not .. we still learn from this. It's a positive direction .. good work!
I LOVE the IQueriable magic - that has solved so many probs for me. Great to see you're a fan of that (still). Adding the ISession is also something that i'm sooo gonna do now - great stuff! I used to have two methods in my Service project (Create and Update), which ended up calling a single Save method on the repository. I can now clean this up even further with the UnitOfWork pattern.
A few questions: (which i'm guessing will be answered in #26 and #27)
*) What happened to 'pipelines' ? I'm now using WF because of what i learnt from your vids. Does this still have a place? I'm guessing there will be seperate workflows per project, as these workflows will be encapsulated within the domain that they are relevant for (eg. order pipelines found inside the OrderProcessing project; sales pipelines found inside the Sales project.. etc).
b) why did u call it ISession and not IUnitOfWork ? (i'm not techincally familiar with the UoW pattern, even though i understand how i've been using it in Linq2Sql).
Hopefully we don't need to wait as long for #26 and #27 (with all due respect). I'm also curious to see how some POCO items will be handled across a number of projects to minimize duplication but to avoid polluting the Infrastructure project.
Lastly, is it worth adding an extra line on the first slide -> Storefront discussion forums: http://forums.asp.net/1165.aspx ?? :)
I don't mean to beat a dead horse, but I totally disagree with Dave Laribee defending that DDD is a set of patterns. Sure, there are lots of patterns like repository, unitofwork, factory, specification, etc... but they are just a means to an end which is to write code that captures the essence of the business. DDD is a perspective or philosophy even, not a set of techniques.
I liked Scott Gu's use of the term "impedance mismatch" applied to the rift that exists between the business concepts and the software concepts. Business people think in their language and when they come up with a business idea, it is an extension of their language. So, if your code reads like their business language, the new features are a snap because they extend naturally from your design.
I'm looking forward to the next two episodes! FYI listen to yourself on hanselminutes, you sound just as vague and heady as the rest of us describing DDD! Ha!!!
Define "set of patterns" how you will. At a certain point we get into a semantic argument unless we agree to call a pattern anything that's repeatable that we've noticed is good. DDD, the book/approach/philosophy/whatever, is a set of patterns in this light.
All that said, it doesn't really matter how we cut it. The main point is that we can take a number of things and apply them to this project or that. In doing so we should be sensible and not necessarily dogmatic or doctrinaire about our approach. What works for this won't work for that, etc.
ps- i'm on cable modem broadband and never had an issue downloading anything before.
I'd thus recommend that before trying DDD you really think about how far they want to go with it. If the problem is simple and not suited to DDD then being too rigorous with it can be silly, having said that I still think some of the patterns like aggregate/repository/service/entity/value object can apply even if the overall project does not suit DDD.
Wouldn't it be better for Rob to use DDD and fail to teach the community something anyway?
This project is about learning and sharing. If you have a project that is similar in size then it's your choice to use it or not.
I'm sick of hearing this project isn't big enough non sense, Rob has gone to too much effort to learn this stuff..
Why don't we all go back to the dark ages and argue about which Text Editor is the best?!?!?!?!
I'm not having a go at you Colin just everyone in general shooting down the idea.. what's the worst that could happen really?
The amount of effort Rob has put in has nothing to do with whether it's a project that DDD is suited to. I'm also not sure that the fact that you are "sick of" hearing something is necessarily a compelling argument. Also note I didn't say it wasn't "big enough", I've worked on a big but relatively simple project and although we did use some DDD patterns it certainly didn't just the full DDD approach.
Back to the original point, my main point was that Eric Evans has indicated that DDD is not for every project. If you think he is incorrect then that's perfectly valid of course.
> Why don't we all go back to the dark ages and argue about which Text Editor is the best?!?!?!?!
Not even sure what this means, I dunno when these dark ages were and why people were arguing about text editors but it doesn't seem related to my point. Also all the "?!?!?!?!" is totally un-necessary and a little childish.
> I'm not having a go at you Colin
I sense this is not true?!?!?!?!?! :)
About Oxite
http://visitmix.com/Lab/Oxite
Source
http://www.codeplex.com/oxite
also of note is
oomph - a microformat toolkit (hCard, hCalendar)
http://visitmix.com/Lab/oomph
dataContext.GetTable<oxite_MessageToAnonymous>().InsertOnSubmit((oxite_MessageToAnonymous)messageTo.MessageToAnonymous);
dataContext.oxite_MessageToAnonymous.InsertOnSubmit (messageTo.MessageToAnonymous);
BTW they're the same because oxite_MessageToAnonymous is a property that just calls GetTable<oxite_MessageToAnonymous>(). I just wasn't sure why they chose to use the more verbose form.
These are all great ideas and I can't wait to look over the code. I'm still hazy on exactly the separation of where a repository stops or starts. In my current project I am dealing with shipments. On the database side there are 5-6 different tables each with their own class / mapping in NHibernate. On the other hand, I don't want the shipment to care. I want to create it, ask the user for some information, and then process it.
So this leaves me with two questions I need to examine (in either your code or future screencasts -- this was actually my first one although I've read your blog awhile!):
1) Is the repository responsible for concatenating these 5-6 tables and spitting out a 'shipment' object, or does the repository spit out the 5-6 tables and then a different layer groups them and spits out a 'shipment' object.
2) The 'shipment' class is going to have some values to show the user, some empty values to ask the user, and some values that simply get translated / worked on behind the scenes. Figuring out how to deal with the UI layer is troubling. Do I have another object that the 'shipment' class gets translated to just for the UI? That would mean a shipment exists in 3 sort of contexts -- what a user sees, what the application sees, and what the database sees. Maybe this is right, or maybe the application and user should be the same?
I like your stuff very much. It's been an inspiration for me during my learning process of ASP.NET MVC and DDD.
hmm ... well, J thought you might be interested in this post when I read it via a link in the ALT.NEt Yahoo mail group:
http://the-software-simpleton.blogspot.com/2008...
Keep on going on, you are on the right track! I have been using OO development for a while and DDD helps a lot bringing the concepts to reality and managing complexity.
I have to agree with Colin, Damien and others on the use of an EntityBase super class and also an IAgregateRoot interface You will get its benefits when you implement your repository.
Its like meditation; don't listen to the noisy ones. :-)
I really admire your dedication and enthusiasm.
Santos
So, are TDD and DDD competing methodologies? Maybe (I don't want to start a religious war - this is based on my experience using both), but I have found that they can both be leveraged to create a form of agility that will help you in reaching for a 'supple' design. Start by designing the domain model first using DDD, then use TDD to validate the design. Before writing a single line of 'implementation' code, make sure the tests feel smooth. Why does this help, it helps to see the big picture by using DDD and having that conversation with your customer. By utilizing TDD, you validate your model will work without too many difficulties. TDD will expose areas that don't feel 'clean' when implementing your scenarios, which is an indicator to go back to the design and clean it up. You may want to iterate over each bounded context, build the Sales model first, validate it, then move on to OrderProcessing. (BTW, TDD for code coverage is a flawed methodology, but that's another religious war I don't want to get involved with)
Next, regarding your OrderState implementation. Is it reasonable to say that an order state is responsible for refunding an order? I get the "smell" feeling you mentioned, which probably means no. The process of refunding an order is not the responsibility of the order state in this context.
To contrast, think of a workflow and it's state. The WorkflowState can keep track of things like: can the workflow start, is it started, is it completed, etc... But should that state be responsible for starting the workflow? Let's take a swag at the code to start a workflow: workflow.State.Start() vs. workflow.Start(). So, just to illustrate this a little more on how the state pattern does helps the workflow object, lets take a look at the framing of a potention workflow.Start() method.
void Start() {
if (!this.State.IsStartable)
throw new InvalidOperationException();
this.State = new StartedWorkflowState(this);
try {
// ... execute the workflow
this.State = new CompletedWorkflowState(this);
} catch (Exception ex) {
this.State = new TerminatedWorkflowState(this, ex);
}
}
I sincerely hope this helps you and keep up the fabulous work, it is quite inspiring.
Rob, nice article! I wish for these concepts to become broadly adopted. I've been having a hard time promoting DDD amongst my peers... For example, it was just announced that our "business dll" would be named "UBECL" for Unified Back End Common Library...
Personally I was rooting to call it "SAURON" .. one DLL to support 10 applications, one DLL to bring them all, and in the darkness bind them ;)
Sent from my phone. Please excuse brief replies.
/J
The problem you are dealing with just screams for the strategy pattern and actually, it would be quite easy to refactor the code that you already have. You could keep an abstract class (it might as well be an enum) to determine the state of the order but move all the logic to separate objects implementing an interface like IProcessingStrategy or inheriting from an abstract class ProcessingStrategy, which would have all the methods that your current OrderState class holds. You would also have to add public methods like Refund, Return, etc. to the Order class. All you have to do now is create a factory that will provide the necessary strategies when needed and avoid the necessity to make any changes to your Order class in the future. Here's a bunch of code that will probably better illustrate the pattern than the above text:
public enum OrderState {
State1,
State2
}
public class Order
{
public OrderState CurrentState { get; set; }
public void Charge() {
ProcessingStrategyFactory.GetProcessingStrategy(this).Charge();
}
public void Close() {
ProcessingStrategyFactory.GetProcessingStrategy(this).Close();
}
}
public static class ProcessingStrategyFactory {
// probably the only thing you need to modify if you decide to add new strategies
public static ProcessingStrategy GetProcessingStrategy(Order order) {
switch(order.CurrentState) {
case OrderState.State1:
return new FirstProcessingStrategy(order);
case OrderState.State2:
return new SecondProcessingStrategy(order);
default:
return null;
}
}
}
public abstract class ProcessingStrategy {
protected Order _order;
public ProcessingStragegy(Order order) {
_order = order;
}
public abstract void Charge();
public abstract void Close();
}
public class FirstProcessingStrategy : ProcessingStrategy {
public FirstProcessingStrategy(Order order) : base(order) {}
public override void Charge() {
// some logic here
}
public override void Close() {
// some logic here
}
}
public class SecondProcessingStrategy : ProcessingStrategy {
public SecondProcessingStrategy (Order order) : base(order) {}
public override void Charge() {
// some logic here
}
public override void Close() {
// some logic here
}
}
First, I really like the way you're heading with your DDD approach now and since you've asked for it: here's a set of Q's/R's:
- Why are you calling SubmitChanges in the repository? Unless you're using something like a TransactionScope, you're breaking your UoW, or not? You'll probably call SubmitChanges in the end anyway (I supppose).
- Why do you expose the OrderState property on Order? Wouldn't it make more sense to call methods like Submit and Close on an order itself (which than delegates it to it's order state)? (You could also implement the state pattern on the Order itself -> NewOrder, SubmittedOrder, ... with the same virtuals on Order of course)
What do you think?
Can someone explain the difference between unit of work and a transaction? I assume that in most cases the behavior would be the same and it is very easy to rap everything in a TransactionScope.
I think the term UoW is used far too much anyway, especially in today's world where you have things like TransactionScope and everybody trying to achieve persistence-ignorance (which is basically the opposite of what you would do with a real UoW - you don't want to call methods on your UoW to register dirty objects, for example).
1) I used StructureMap 2.5 with the exact pattern you used to create the DB context. When I used InstanceScope.Hybrid a new instance of the DB object was created on the creation of every new repository object. When I switched it to InstanceScope.HttpContext only one is being created per request. Are you seeing the same occurrence? Have you checked to make sure the context is the same for each http request or just assumed it was?
2) I really like your LazyList. So much to the point I used it in my project. I don’t know if you have addressed this, but an object graph containing a LazyList shouldn’t be cached as the internal IQueryable holds a reference to the context (DB) object. As long as the object is cached the context object will hang around even if ToList() has been called on IQueryable reference. From what I understand this might not be a problem with LINQ to sql and EF(sql). However, if you decided to use another ORM with a LINQ implementation the connection objects might not be disposed of properly. Since the connection has already been created and not destroyed or returned to the connection pool, I believe you will run into a problem. If I am wrong please let me know. I know you want to keep web apps as stateless as possible however if it can be done it will be done; and runtime exceptions are the hardest type to debug.
I noticed that TranslateOrder has a comment that an Order should always be in the database. There is then code to handle it not being present. Would it be better to log and throw an exception if it shouldn't happen?
In TranslateCustomer there is similar code to check if the Customer exists in the database. If they do not then a Customer is newed up. The Customer is never added to the _session however (that I can see) and so is lost. Obviously, this could be fixed with an appropriate call to _session.Insert but I still think it may be better that if you check for something that cannot happen that you flag it as an error.
In OrderProcessing.Order.cs you have declared Items as being of type LazyList<OrderLine>. Would that be better if it was IList<OrderLine> ?
Keep up the good work, Paul
They are mainly about code smell, which Rob has identified strongly.
I wish Rob's boss can read all this and ask him to go 100% on MVC SF for a month so he can knock that baby out of the park and we can all celebrate and make awesomesauce websites.
long vacation :) so that's the main reason for the slow down. Well, that and
Oxite :).
My boss is Bertrand Le Roy (bleroy) if you want to petition him for my time
:):):)
I hope others email Bertrand so he (or the powers that be) can see how important this series is and will become. It's our cheat sheet to the taking over the net, one MVC proj at a time.
Thanks for the great series. I've just seen episode 13, but i'm posting here anyway.
You are actually helping me stay in shape, because I streaming your episodes to the big screen in the living room(xbox 360) where I watch you while working on my hometrainer (hope that didn't sound creepy :-) )
Tell your boss that this is what we want. If he stops listening tell his boss.
I'm toying around with MVC and there is a lot of <%= Html.Encode(Model.Message) %> going around. Which was not the case in asp.net (you could but you should not)
<%= actually means Response.write() so why not change it to <%= is Html.Encode(response.write())
Which you have to do about 95% of the time.
The other 5% can do <% Response.Write("not encoded stuff") %> or <% Reponse.KillerGenericExtensionMethod<T>() %> for the diehards.
That should put a lot XSS baddies out of business.
One thing I'd suggest is using an interface for your domain models/objects instead of creating a whole other concrete class, it just seems... meh. A good example of doing it this way is to take a look at the latest Kigg at http://www.codeplex.com/Kigg. The way they have developed their DAL is fantastic and even contains a UnitOfWork object.
Hope this helps, keep up the good work!
details? Also, with the latest bits I have here, I have Unitofwork built in.
I meant instead of having 2 sets of concrete objects (POCO and LINQ to SQL generated) you could have an interface. Then extend your LINQ to SQL generated objects by using their partial class-ness and than adding the I<EntityName> interface to the partial. This way you can get the functionality you desire without having to do translation for queries. As far as UnitOfWork I was just saying your new DDD version of the Storefront project and the new Kigg have a lot of similarities you could maybe get some ideas from.
When will we get a chance to get your latest code base? I'm really interested to see the code for your state pattern implementation.
though and say that it ties me a little too closely to Linq to Sql. In other
words my domain bits will rely on ORM-generated code. I also can't work with
"value objects" really (like Address) which exist in the DB and would have
gets/sets (even though I'm not fully embracing that now :).
Either way - good stuff. If you have a blog I'd love to see more on the idea
since overall it's a nice pattern. RE Kigg/DDD I haven't seen their code but
I did manage to work up a nice UoW thinger for what I'm doing.
I hope to have the new source up in the next few days.
http://weblogs.asp.net/chadmoran/archive/2009/0...
I know you don't want to be tired to ORM-generated code and you don't have to be if you setup some IoC and spread out your assemblies properly. I agree that you should stay loosely coupled to your DAL so that you code can be very agile. However, I think the interface contracts for domain objects has it's benefits. I'm not against using POCO objects I just think having to translate your queries between the objects can get really messy. I tried following your series step by step to better help me understand the code and I just couldn't stand it when I had multiple one-to-many and many-to-many relationships, I felt like I was writing SQL for something that already was.
Maybe it's just me but unless you have security issues I think you can still stay loosely coupled and still achieve your goal of not dependong on ORM generated code... you have to somewhere.
Hope this helps and can't wait to see your code, nice house by the way.
Im just after a practical approach to the documentation that gives a written document not a video. As useful as your are in explaining and bringing together a sensible approach to the technology at our finger tips, I would like to see you apply that same practical common sense to a documentation effort.
have to). Generally people aren't very interested in watching me write docs
- they want to see the code and learn how to do things; thus the videos.
I already see evidence of well thought out architecture and well am impressed frankly.
I like your approach and your results. I am not assuming you won't do any doco.
I am raising it as something I would like to see.
I would like to see both your approach and your results to the doco side of things.
Keep up the good work.
anything until I'm finished :). Too many spent cycles writing docs that get
outdated the next day.
I have a whole episode I want to do RE "Being a good Dev - Let's Analyze and
Document" etc.
I'm hard at work on this stuff and am working on something pretty dang cool
at the moment :)
It is hard to wait when you have no estimated time, though.
WTB ASP.NET MVC RC! (that's a lot of acronyms)
/me drools at how close we are learning from this project once more!
asking you, be patient.
Good job with this series. I have learned allot from your masochistic self review. It provides something I have never seen in any dot net blog before : perceptive. Something that is worth allot more than any "Coders Morality Of The Day". Let’s face it, the nay Sayers just don’t have the balls for it.