Today, many projects focus on Domain-Driven Design, but it is not always easy. One of the most important things are to separate the domain code from the code that only exists for technical reasons.
Mats Helander has written an InfoQ article about how to manage domain models with a concept he calls Domain Model Management. In the article, Mats leads the reader step by step though common problems regarding design and separation of concerns when trying to implement a domain model, explains ways to solve the problems and manages to teach aspect oriented-programming, a couple of design patterns and some things about object/relational mappers in the process.
In this excerpt from the article introduction, Mats reasons about where to put the infrastructure code:
As the amount of infrastructure code grows, finding a good architecture for dealing with it all becomes increasingly important. A big part of the question is - are we allowed to put some infrastructure code in our domain model classes or is that something which should be avoided at all cost?
The argument for avoiding infrastructure code in the domain model classes is strong: The domain model is supposed to represent the core business concepts that our application is dealing with. Keeping these classes clean, lightweight and maintainable is an excellent architectural goal for an application that intends to make heavy use of its domain model.
On the other hand, as we will go on to see, taking the extremist route of keeping our domain model classes completely free of infrastructure code - often referred to as using a Plain Old Java/CLR Objects (POJO/POCO) domain model - can also prove problematic. It will often result in clunky, less efficient workarounds - and some features just won’t be possible to implement at all that way.
This seems to indicate, as is so often the case, that we have a trade-off situation on our hands where we should try to put only just as much infrastructure code in our domain model classes as absolutely needed, but no more. We trade off some bloat in the domain model for some improved efficiency or enablement of some required Domain Model Management feature that wouldn’t work otherwise. Negotiating good trade-offs is, after all, a large part of what software architecture is all about.
Lean back and enjoy the full article about Aspects of Domain Model Management, and the accompanying source code.
Community comments
Bugs in article
by Mats Helander,
Nice article !
by Joel Costigliola,
Re: Nice article !
by Mats Helander,
Re: Bugs in article
by Diana Baciu,
Great article!
by Steve Macdonald,
Re: Great article!
by Mats Helander,
Good article
by Kristof Jozsa,
Re: Good article
by Mats Helander,
Re: Good article
by Kristof Jozsa,
Re: Good article
by Mats Helander,
Well Done!
by Perry Hertler,
Re: Well Done!
by Mats Helander,
Excellent article!
by Aslam Khan,
Re: Excellent article!
by Mats Helander,
Intercepting Domain Objects
by Christian Tzolov,
Much content, little sense
by Steven Devijver,
Re: Much content, little sense
by Greg Helton,
Re: Much content, little sense
by Mats Helander,
Typing
by Niclas Nilsson,
Re: Typing
by Mats Helander,
Re: Much content, little sense
by Aidan O,
Re: Much content, little sense
by Mats Helander,
Re: Much content, little sense
by Mats Helander,
Good and clear !
by Nicolas Mousson,
Java Listings available
by Aslam Khan,
Re: Java Listings available
by Christian Tzolov,
Re: Java Listings available
by Mats Helander,
Re: Java Listings available
by osman yılmaz,
Re: Java Listings available
by Aslam Khan,
The point is how to bind the domain object to the mgmt infrastructure...
by Dan Haywood,
Keeping persistence out of the domain
by John Unwin,
Re: Keeping persistence out of the domain
by Dan Haywood,
Re: Keeping persistence out of the domain
by Mats Helander,
Re: Keeping persistence out of the domain
by Mats Helander,
Dirty Tracking only on Setters? That won't work!
by Udi Dahan,
Re: Dirty Tracking only on Setters? That won't work!
by Mats Helander,
Re: Dirty Tracking only on Setters? That won't work!
by Jon B,
About abstract factory and domain logic
by Zoran Horvat,
Bugs in article
by Mats Helander,
Your message is awaiting moderation. Thank you for participating in the discussion.
Even though I proof-read the article several times before submission, I'm still able to spot a few bugs in it now. I will continue to update a blog post for this article at the following address with bugs as I find them:
www.matshelander.com/wordpress/?p=80
I will still list the bugs I have found so far at the end of this comment.
Also, while this may be my mistake, I don't see the link to the code? At any rate, you can download the Visual Studio project with the code for the article here:
www.matshelander.com/infoq/Infoq.AspectsOfDMM.zip
Thanks,
/Mats
List of Bugs:
List 2
The Persistence component should not contain any dirty flags.
Figure 6 (text above)
The text states that the person class inherits a dirty flag it doesn’t need, but it should be that it inherits a lazy flag that it doesn’t need.
Figure 7, 10, 11, 12
The EmployeeProxy class should have getName and setName methods (just like the PersonProxy class) overriding the ones on the Employee class in order to provide the interception.
List 4
The first property in the EmployeeProxy class misses its name. Currenlty it just says “public override string”, it should be “public override string Name”.
List 5
In the setter method for the Salary property in the EmployeeProxy class, the call to the OnPropertySet method on the dirtyInterceptor sends “this.name” as the second parameter – it should be “this.salary”.
All the calls to the lazyInterceptor send along a second parameter with the property name – but the interceptor method only accepts one parameter (and doesn’t need a second one). Thus all the calls to the lazyInterceptor should be changed to: “lazyInterceptor.OnPropertyGetSet(this);”
List 7
The Person class should no longer inherit from the DomainBase class (which has been deleted).
Nice article !
by Joel Costigliola,
Your message is awaiting moderation. Thank you for participating in the discussion.
Hi Mats,
Very nice article, describing well a way to get a clean OO design, and introducing how AOP can help !
I have spotted some mistakes (no big deal, the article is easy to read anyway), concerning list 5 code not completely faithful to figure 11.
Figure 11 / List 5
- ILazyLoader / ILazy
- LazyLoaderMixin / LazyMixin
- LazyLoadingInterceptor / LazyInterceptor
Well as I said, no big deal, just some "lazy" mistakes ;o)
Regards,
Joel
Re: Nice article !
by Mats Helander,
Your message is awaiting moderation. Thank you for participating in the discussion.
Hi Joel,
Thanks for the kind words and for the bug spottings! :-)
/Mats
Re: Bugs in article
by Diana Baciu,
Your message is awaiting moderation. Thank you for participating in the discussion.
Hi Mats
i've added a link to the code at the end of the article.
diana
Great article!
by Steve Macdonald,
Your message is awaiting moderation. Thank you for participating in the discussion.
Mats:
This was one of the most useful articles I've come across in a while. I've circulated it amongst my peers. There is a real need for people like you who can communicate these critical ideas so concisely and clearly. In our shop we basically live and breathe Evans, Fowler and Nilsson. Looks like we'll be adding Helander to that list!
Happy New Year!
Steve
Re: Great article!
by Mats Helander,
Your message is awaiting moderation. Thank you for participating in the discussion.
Wow...thanks Steve! I really don't know what to reply to such kind words, I'm all embarrased now :-P
Reading your comment certainly made a great start on my new year! A very Happy New Year to you as well! :-)
/Mats
Good article
by Kristof Jozsa,
Your message is awaiting moderation. Thank you for participating in the discussion.
Hi Mats,
having read your article word-by-word, I'd say good work. Please allow me some constructive comments.
The article [imho] is about 4 times longer than it should be based on the content. I also couldn't help thinking "omg, this guy's crazy" at least 5 times during reading the article (especially around introducing subclass hell with code examples and claiming it's a good approach). If the article's title or introduction warned me about showing evolutionary thinking to introduce AOP, you could have spare this :) My bet would be that many readers left in the middle of the article thinking you have no idea at all about OO design.. Anyway, nice work indeed!
Well Done!
by Perry Hertler,
Your message is awaiting moderation. Thank you for participating in the discussion.
Thanks for taking the time to write this great article, which lays a good foundation for AOP concepts while showing the problem it solves.
One comment: In the real world the Dirty and Lazy mixins would almost certainly need to be thread safe.
--
Perry
Re: Good article
by Mats Helander,
Your message is awaiting moderation. Thank you for participating in the discussion.
Hi Kristof,
Thanks for the feedback,
Sorry I wasted your time...I assure you that if I knew how to write more succinctly, I would! ;-) But even though the title perhaps didn't reflect this, I think I did explain in the introduction what would come (a refactoring to aspects)? But you are certainly right that it's a long text and if I didn't do a good enough job of giving the reader accurate expectations of the content from the introduction, then that is indeed a severe issue, so thanks for pointing it out!
I googled "subclass hell" to see if this was a widespread opinion with its own term, but only got one hit with that usage. I'm not sure I know what you mean by the term but I assume you mean "getting an explosion of subclasses" kind of thing? Again, as was the point of the article, this is only an issue (if even then) should you decide to code your subclasses manually, which is not necessary at all - they can be easily generated at design time or runtime. But I would love some more info on this perspective - for example, if a bunch of subclasses is considered more "hellish" than an obese domain model (bloated domain classes) or having to use inefficient workarounds such as using original values to get dirty status - and if so, why? Do you have any links to discussions attacking the architecture I suggest on the basis of the proliferation of subclasses?
"My bet would be that many readers left in the middle of the article thinking you have no idea at all about OO design"
Given your objections above ^^ (subclassing and some other instances of thinking I'm crazy (that I'd love to know more about as well)) I didn't quite understand if this was your opinion as well (that I have no idea about OOD) or if you think the suggestions I make in the article aren't actually crazy - only somewhat poorly explained?
/Mats
Re: Well Done!
by Mats Helander,
Your message is awaiting moderation. Thank you for participating in the discussion.
Hi Perry,
Thanks for the nice words. :-)
You're absolutely right about the thread safety of course and I should have pointed it out in the article - great point, thanks!
/Mats
Re: Good article
by Kristof Jozsa,
Your message is awaiting moderation. Thank you for participating in the discussion.
Hi Mats,
you didn't waste my time, I just thought this content can be explained in a more compact form :) I don't think 'subclass hell' is a wide-spread known term but I guess you got what I meant: the sperodic grow of number of subclasses around a project without a business reason. Generally I believe good OO design mostly prefers composition in favour of subclassing. (I guess it's worth to mention that by 'subclassing' I mean the 'manual' way, subclasses generated by an AOP framework wouldn't count this way (it's rather a technical solution not subclass by design). At last, after finishing reading the article, I *do* think you understand OOD.. sorry if I wasn't clear about that.
Kristof
Excellent article!
by Aslam Khan,
Your message is awaiting moderation. Thank you for participating in the discussion.
Hi Mats,
Really nice article that pulls together a lot of the OO patterns cleanly. This is certainly one of the better coverages of AOP that I have seen. It certainly de-mystifies AOP and makes it attainable for the masses.
Nicely done!
Intercepting Domain Objects
by Christian Tzolov,
Your message is awaiting moderation. Thank you for participating in the discussion.
Nice article Mats,
IHMO it is more academic then applicable for real applications. Many java enterprise applications apply technologies like Spring and Hibernate that makes it rather difficult to maintain and test Domain Objects that have been instrumented with proxies or AOP. You might not be able even to plug your AbstractFactory implementation into the persistence layer.
Although the DOs can be instrumented by AOP this looks like breaking their light-weight nature and making the testing dependent of heavy frameworks.
Much content, little sense
by Steven Devijver,
Your message is awaiting moderation. Thank you for participating in the discussion.
This article starts by describing how to re-write hibernate and then Spring.
Important challenges for domain-driven design are design-related, not technical.
Persistence is never part of domain-driven design. A domain model solves a specific problem in the problem domain. How state is loaded from and saved to the database is not part of that problem.
In the end, persistence is a sub-problem of how to convert between different domain models in the same system. The domain model used for persistence is hopefully a different one from that used to solve hard domain problems.
Why not make use of context maps?
Erik Evans says: not everything is a large system will be well-designed, so let's try to create a good design for at least the core.
Introducing ORM invariably hurts the design of your domain model and those classes that are affected should best be considered as part of the not-well-designed-part of a system. It's up to each one of us to figure out ways to cope with that.
It would assume however that AOP would seldom be a solution. AOP typically adds behavior where it's not yet available. By modifying your core domain classes with AOP you run the risk of moving from the well-designed area to a less-well-designed area.
Is that what you want? Is persistence so critical that it's allowed to hurt the most fundamental domain classes?
Good and clear !
by Nicolas Mousson,
Your message is awaiting moderation. Thank you for participating in the discussion.
Hi Mats,
Very nice and clear ! I don't think the article is too long, because you explain in details the concepts and problems so clearly, that it's very nice and easy to read.
Just one question : I have tested "PostSharp Laos" AOP solution, and it's very easy to use (especially for people who begins with AOP). Have you already used it ?
Thanks !
Nicolas
Re: Good article
by Mats Helander,
Your message is awaiting moderation. Thank you for participating in the discussion.
Hi Kristof,
"Generally I believe good OO design mostly prefers composition in favour of subclassing"
I couldn't agree more! In fact that position is pretty much the underlying premise of the whole article! We refactor from using inheritance where reusable behavior is placed in a common base class to using composition where the behavior is placed in mixins. The subclasses are just the "glue" - their only purpose is to implement the composition! And the only reason we put this composition code in a subclass is so that we can keep the domain class free of infrastructure. We /could/ skip the subclass and put the composition code directly in the domain class, in which case it is really clear that the core message of this article is to use composition over inheritance. Taking that message "all the way", in my opinion, leads to AOP.
Hmmm. I guess you were right I could have explained that in a more compact way than in the article. Seems about seven sentences could have cut it ;-)
Thanks again for the feedback! :-)
/Mats
Re: Excellent article!
by Mats Helander,
Your message is awaiting moderation. Thank you for participating in the discussion.
Aslam, Christian and Nicolas - thanks guys! :-)
Christian,
Yes you have a very good point - it ties in a bit to my reply to Steven, but in essence what you address is the next thing I would have turned to in my article if it haden't already been way too long...in fact I wrote a first version of the article covering /only/ that, but it became hard too understand for anyone not already intimately familiar with the subject. Starting to write about the necessary background for understanding that article, I ended up with the current article.
Anyway, without repeating the whole first version of the article in the comments, my opinion is that this is a really interesting topic that will become increasingly important in the future: Which component gets to control object creation and configuration? And furthermore - could components share a set of common infrastructural aspects? Could, say, Hibernate use some standard lazy loading aspect from an aspect library instead of providing its own? I will be returning to this topic in my blog shortly, if you're interested in more of my ramblings on the subject. ;-)
Nicolas,
Nope I haven't tried PostSharp Laos but I will now! :-)
/Mats
Re: Much content, little sense
by Greg Helton,
Your message is awaiting moderation. Thank you for participating in the discussion.
Thank you, Steven Devijver. You are absolutely correct. The article and code ignores the value of a layered architecture and dependency inversion. This article is design pattern code smell as described in www.relevancellc.com/2007/5/17/design-patterns-...
The author has made the design pattern into a recipe.
Re: Much content, little sense
by Aidan O,
Your message is awaiting moderation. Thank you for participating in the discussion.
Nothing in the article comes close to rewriting any part of Hibernate! Also, it's written in a fairly language/platform agnostic way, so while reading it I assumed I'd be using Spring in my own implementation for the AOP related stuff.
A very well written article IMO.
Java Listings available
by Aslam Khan,
Your message is awaiting moderation. Thank you for participating in the discussion.
I put together a really quick set of Java equivalent listings of the C# lists in the article. For AOP, I used SpringFramework 2.5 and AspectJ. You can download the zip from my blog.
Use the sources at your own risk. It compiles cleanly but I have not proofed it for correctness. The intention is just to give a view from Java-land within the perspective of this article. The classes and interfaces for each listing are contained in their own package, viz. infoq.dmm.list1, ..list2, etc.
/Aslam
Re: Java Listings available
by Christian Tzolov,
Your message is awaiting moderation. Thank you for participating in the discussion.
Hi Aslam,
The Domain Objects being advised (e.g. Person, Employee) are not Spring beans created though an application context. Instead the instances are created at run-time through the AbstractFactory's. Therefore the tricky part with the above java translation is that it requires a full-blown AspectJ through the Load-time weaving with AspectJ in the Spring .
Although valid this approach violates the simple POJO development style. You are no longer able to write simple JUnit tests because your domain objects depend on Spring, ApsectJ and LTW.
/Chris
Re: Much content, little sense
by Mats Helander,
Your message is awaiting moderation. Thank you for participating in the discussion.
Hi Steven,
Thank you so much for your excellent feedback! I will try to address your points as best I can:
"Important challenges for domain-driven design are design-related, not technical."
Completely agreed - as far as it concerns the design of the domain model, all design desicions should be motivated by the shape of the domain, not by infrastructural concerns.
That's just what this article is about - finding ways of moving all the pesky, inevitable infrastructure out of the domain model classes so that they can remain clean and domain focused. This article looks at how to find an architecture that will help deal with the infrastructure - it does not go into the topic of how to design your domain models to make them appropriately represent the domain (more so than noting that it will probably be easier for you to do that activity if the domain model design can be kept free from infrastructural concerns).
"Persistence is never part of domain-driven design."
Agreed - persistence should not influence the design of the domain model.
But my focus is not on persistence. In fact, the article tries to take the opposite perspective.
While today many may only use aspects on their domain model unwittingly if their O/R Mapper uses aspects under the hood to provide features such as lazy loading (the way for example N/Hibernate and NPersist does) the main point of the first version of this article (that I mentioned in my reply to Christian) was that this shouldn't be a necessary state of affairs. Indeed, many of the aspects an O/R Mapper might apply (such as inverse property synchronization) could make sense to use even if the domain model didn't have to be persisted at all (something I also touch more upon in the blog post about DMM, linked above at the end of the article).
So while I agree with you that most of the stuff I describe in my article is probably /currently/ academic (as Christian suggested) to anyone who isn't building an O/R Mapper, I feel that it would be to waste a lot of opportunities to continue to see it this way. I believe I see why you associate this to an article about "building Hibernate" (and to some degree Spring) - and I think it is a very sharp observation, it is just that I would argue that in the long run, seeing this as something that has to do just with persistence would mean passing on the opportunity to exploit the same structures that O/R Mappers often use to deal with this particular type of infrastructural demands.
"Why not make use of context maps?"
Because the article addresses how to manage a domain model within one context - I fully agree with you that if the model required for persistence looks very different from the domain model you want to use for your business logic then splitting them into two separate contexts with one model each and using a context map to keep track and transform between them is a great option. But as I said above this article isn't about persistence - even though it talks about the same kind of aspects that a persistence component would often be the primary (but not necessarily exclusive) consumer of.
But let's say we have a Domain Context and a Persistence Context - each with its own version of the domain model - and a transformer that can transform between the two models. I think what you're saying is that in that case, only the model in the persistence context would need the kind of things I'm talking about in this article. In that case, I would disagree - I think there are many different components, living in many different contexts, that could make use of aspects on their models.
But I do agree that currently we don't see a lot of this outside the world of O/R Mapping...again, if the article had not been so darn long already, this is exactly what I would have liked to go on to discuss, so I can't tell you how happy I am about the critical feedback from you and the others here in the comments section! :-))
"Erik Evans says: not everything is a large system will be well-designed, so let's try to create a good design for at least the core. "
Right, and this article is all about moving the infrastructure out from the core, to keep it clean.
"Introducing ORM invariably hurts the design of your domain model and those classes that are affected should best be considered as part of the not-well-designed-part of a system. It's up to each one of us to figure out ways to cope with that. "
An interesting point when you make it that general and one that I would love to discuss with you (preferrably over beer) :-)
I disagree with you to some extent - well, only on the "invariable hurts" part, really...Nonetheless, I would argue that it isn't what this article is about, as explained above. However, since I've written a few O/R Mappers I'd love to discuss this with you in some other thread! But perhaps you only mentioned it as a segway into:
"It would assume however that AOP would seldom be a solution. AOP typically adds behavior where it's not yet available. By modifying your core domain classes with AOP you run the risk of moving from the well-designed area to a less-well-designed area."
This certainly has very much to do with the article - and here we disagree completely. I think AOP helps move infrastructure (which is what so much of the cross cutting concerns in an application is about) out of the domain model, with two distinct wins:
1) The domain model can be kept clean and domain focused
2) The infrastructure code can easily be written in a modular, generic and reusable way.
"Is that what you want? Is persistence so critical that it's allowed to hurt the most fundamental domain classes?"
On the contrary - I don't think neither persistence nor /any other components/ that might need the types of cross cutting, interception and introduction based infrastructural features (and I would argue that persistence only broke this ice because persistence needed these features the most, first) should be allowed the domain model! This whole article is about attempting to find ways of solving that very issue.
But I think the core of the point you make here is that you're saying that AOP would hurt the design of the target (model) classes...I don't really see why this would be the case? Even with the architecture suggested in this article before an AOP framework is used, while it is of course inconvenient to write the subclasses manually, I don't see how the surrounding infrastructure hurts the domain model, which is kept completely unwitting of the whole thing.
If you could please elaborate on how you see AOP as potentially hurting the design of the target classes I think that would be enlightening to the discussion?
Otherwise, if you agree with me that any such damage is really at worst pretty minimal (such as the need to use AbstractFactory if we're using a runtime subclasser) do you also agree that in that case using AOP doesn't actually conflict with the goals of DDD?
/Mats
Re: Much content, little sense
by Mats Helander,
Your message is awaiting moderation. Thank you for participating in the discussion.
Hi Greg,
Thanks for the feedback.
I do agree to some extent with you and others who say that design patterns often represent shortcomings in the programming language. I do mention in the article that some languages, such as Ruby, support features that makes it even easier to implement the core concepts that I'm trying to advocate in this article - reusable infrastructure in the form of mixins and interceptors (favoring composition over inheritance) applicable in any of several different ways including manual subclasses, AOP frameworks and indeed features of some very dynamic languages such as Ruby.
It may be that we agree on the principles this article is actually about - finding ways to make infrastructure code reusable and accessible to many parts of the application without unduely increasing the coupling - but that you react on the admittedly kludgy implementation required in languages such as Java and C#, which does indeed depend on a couple of well known design patterns that are targeted for just these types of languages but aren't really needed in other languages.
I won't argue with you there. But on the other hand, part of the idea behind the article is to show that these concepts are in fact compatible with stongly typed OO - even if the implementation isn't as slick as in Ruby.
However, I'm not sure I follow what you mean by: "The article and code ignores the value of a layered architecture and dependency inversion."
Since I'm not sure what your argument is here I realize this doesn't answer it, but I will note that I'm using the architecture suggested in the article together with a layered architecture and dependency injection, so as far as I know it doesn't ignore nor is it incompatible with with these things...but I'd love if you could elaborate a bit on this.
/Mats
Re: Much content, little sense
by Mats Helander,
Your message is awaiting moderation. Thank you for participating in the discussion.
Hi John,
Thanks! :-) Indeed the things I actually try to advocate in the article (reusable DMM infrastructure using mixins and interceptors) are supposed to be platform agnostic, even though the discussion about how to apply these ideas in a strongly typed OO language such as java/C# is of course not really equally agnostic (as noted by Greg).
/Mats
Re: Java Listings available
by Mats Helander,
Your message is awaiting moderation. Thank you for participating in the discussion.
Chris,
"You are no longer able to write simple JUnit tests because your domain objects depend on Spring, ApsectJ and LTW."
Assuming that the behavior under test is depending non those aspects being present - which it typically shouldn't be, if we're able to keep our domain model classes free of infrastucture concerns (that are put in aspects). Likewise, the mixins and interceptors that form the aspects should preferably also be easily testable in separation. The test you mention would be the test checking that everything worked together.
/Mats
Re: Java Listings available
by Aslam Khan,
Your message is awaiting moderation. Thank you for participating in the discussion.
Hi Chris
Yes, your observation is, to a certain extent, valid. However, the intention was not to get too involved with the SpringFramework but to stick to the theme of the article; i.e. to produce domain classes with minimal infrastructure code, and delegate infrastructure code to other classes.
For me, one of the more important side effects of this article is that you can use simple POJO-based unit tests to test your domain classes and domain rules and logic. Likewise, the infrastructure code is then also easily testable, independent of the domain classes. Just what we want we're aiming for. Like the article suggests, AOP is just a convenience mechanism that does the proxying for you so that you don't have to write all those proxy classes by hand.
Bottom line: make sure that we don't litter our domain classes with infrastructure code from the proxy classes, factory classes, etc, and vis versa.
The kind of testing that you refer to deals more with, what I call, integration testing, i.e. to make sure everything hangs together correctly. In this case, Spring offers convenient ApplicationContext aware JUnit test classes that can be extended for such integration testing.
/Aslam
Typing
by Niclas Nilsson,
Your message is awaiting moderation. Thank you for participating in the discussion.
And Mats of course means /statically/ typed, not /strongly/ typed when he talks about differences between Java/C# and Ruby, right Mats? ;-)
Kind regards
Niclas
Re: Typing
by Mats Helander,
Your message is awaiting moderation. Thank you for participating in the discussion.
Hi Niclas,
Yes I do, thanks for the correction!
The point is how to bind the domain object to the mgmt infrastructure...
by Dan Haywood,
Your message is awaiting moderation. Thank you for participating in the discussion.
... and you are right that AOP is a nice way to extract out this cross-cutting concern. However, the cost of using AOP is either:
I read your article from the viewpoint of how Naked Objects Framework accomplishes this same requirement (my background being that I was part of the team that delivered a 700+ user enterprise system running on Naked Objects). The standard approach for NOF does require a small amount of "obeseness" in the POJOs. Specifically:
The responsibility of the Customer domain object is to (a) ask the DomainObjectContainer to lazily load its properties and (b) to inform the DOC when it has changed. The DOC itself is injected into the DO when it is initially pulled back from the RDBMS. As you probably have realized, the DOC is just an interface that is implemented by the NOF.
My view is that this is a pretty minimal dependency. Of course, you can imagine it's easy enough to write an aspect to completely remove the requirement for the resolve() and objectChanged() calls if desired. Another alternative would be to use CGLib to enhance the DO's bytecode, in the same way that Hibernate does. So long as the DOC gets called, mission accomplished.
As a side note on implementation, the NOF itself wraps each DO in an instance of NakedObject. It is this that manages the lazy and dirty states. Note though that the NakedObject isn't a proxy, rather it is completely generic. The NOF viewers access the metamodel (to paint the generic UI) via this NakedObject wrapper.
Cheers
Dan Haywood
Keeping persistence out of the domain
by John Unwin,
Your message is awaiting moderation. Thank you for participating in the discussion.
There is a common view, reinforced by this article, that a domain model should be completely ignorant of persistence. Taken to it's logical extreme it results in the convolutions introduced by Mats in this article.
How about a sensible compromise...
These responsibilities sit comfortably in the domain model. To put these responsibilities elsewhere results in the distribution of business rules among artificial constructs such as "repositories", "Data Access Objects", etc.. The proxies, awkward inheritance hierarchies, etc. that Mats proposes are more such artificial constructs.
So how about this for a solution:
Dirty Tracking only on Setters? That won't work!
by Udi Dahan,
Your message is awaiting moderation. Thank you for participating in the discussion.
First of all, let me commend you on the thorough article. I really enjoyed it.
Now on to the meat...
As we've discussed in the past, handling concurrency requires fetching an object before changing it, just as a reminder, here's my post on the subject: Realistic Concurrency.
In this model, no setters are called on domain objects. Instead, when a method is called, the method changes the fields on the object.
If you were to do dirty tracking by keeping a copy of the object, you'd know that the object was changed. Since you cannot assume that every method changes an object, I don't see how you could use the interception technique to have it handle this scenario.
Interested in hearing your response.
Re: Keeping persistence out of the domain
by Dan Haywood,
Your message is awaiting moderation. Thank you for participating in the discussion.
I'd agree with those, and this is exactly what Naked Objects does. For example, one can annotate properties and actions with @Disabled(When.UNTIL_PERSISTENT).
In Naked Objects terms this would be getContainer().disposeInstance(this);
Well, Naked Objects used to work this way. However, the trouble is that one cannot override or mock such methods. In NOF 3.0 we now use repositories which are registered with the framework and which are instantiated as singletons. These are injected into every domain object that need them, and are accessible (if appropriate) directly from the UI. Putting this functionality as instance methods means that we can then override or provide different implementations (eg running against an in-memory DB rather than a fully fledged RDBMS).
So instead of your proposed solution one would have:
Yup, this is exactly what Naked Objects does. Hibernate is used for the back-end persistence.
Re: Dirty Tracking only on Setters? That won't work!
by Mats Helander,
Your message is awaiting moderation. Thank you for participating in the discussion.
Hi Udi,
Thanks, glad you liked it! :-)
>In this model, no setters are called on domain objects. Instead, when a method is called, the method changes the fields on the object.
Yes, this is an issue - you have to code your methods to be stringent in always going by the getters/setters rather than accessing the fields directly, since the field access can't be intercepted. Again, I would love to see real AOP features in the platforms (Java/C#) but since we don't have that, we'll have to use patterns and procedures to achieve a properly modular design of the infrastructure code.
So, to directly answer your question, you'd have to modify the methods so that they updated the objects using the setters (or write them thusly from scratch, not necessarily a bad idea...what if you wanted to extend your class with one that did something kewl in the setter and then the setter wasn't called when the object was updated by your method?).
/Mats
Re: Keeping persistence out of the domain
by Mats Helander,
Your message is awaiting moderation. Thank you for participating in the discussion.
Hi John,
Well, there are many ways to skin a cat! :-)
Much of what you talk about can be solved using Inversion of Control, such that you get events when objects are persisted or deleted and can hook into your own methods for performing validation or additional cleanup. On the Active Record approach (Save() methods on the domain objects) I agree with you that it can be a very comfortable API to be able to find the persistence methods on the objects themselves - but I would prefer to mix those methods onto the objects using the approach described in this article! ;-)
But bottom line is I don't disagree with you. If you can get away without cluttering your domain model classes more than in your example, I agree it's certainly no big deal - definitely something that you can live with and indeed nothing that motivates the kind of architecture discussed in this article. I'd wholeheartedly agree with your recommendation to use this approach whenever it was certain that the minimal "bloat" you describe was all we would end up with.
My article attempts to examine a long term strategy for dealing with the particular kind of infrastructural complexity that doesn't have the decency to stop at littering your domain model classes with a few persistence related methods. I hasten to add that I'm not suggesting your applications nor the infrastructure for them wouldn't be complex - I'm saying /if/ you find yourself in a situation where the particular types of infrastructure demands of your application pushes you towards a domain model that is increasingly bloated by infrastructural concerns - and you find that moving the infrastructure ocde out of the domain model entirely would result in less efficient workarounds - then using the approach described in this article can provide a way to refactor back to a clean domain model. Some very complex applications don't run into this issue, for sure, but for those that do I think the article provides sound advice on how to deal with it.
Thanks for the great feedback! :-)
/Mats
Re: Keeping persistence out of the domain
by Mats Helander,
Your message is awaiting moderation. Thank you for participating in the discussion.
Hi Dan,
Yes, as long as we don't have good, native support for AOP in the platforms (Java/C# or even JVM/CLR) I agree that each way to write an AOP framework carries its own drawbacks. Personally I think the main drawback with runtime subclassing - relying on the Factory - isn't too bad, since you may well want to have a place to inject a reference to the container/context (just as you mention).
As I said to John, I see no problem with the idea of using AOP to apply interfaces+mixins with persistence methods onto the domain objects. In fact I think it can be a great idea, since it allows all parts of the application to easily access the persistence mechanism - including other aspects.
I think your comment underlines the premise of the article, which is that it can be very convenient to be able to have your infrastructure related features accessible on the objects themselves - including persistence methods! I couldn't agree more with this - it's what the article's all about. :-) In fact, the article looks at what happens when we're so in love with this idea that it starts to seriously threaten the maintainability of our domain model classes...and how we can deal with it.
/Mats
Re: Java Listings available
by osman yılmaz,
Your message is awaiting moderation. Thank you for participating in the discussion.
thanks a lot for useful knowledge
www.vitamins5.com
Re: Dirty Tracking only on Setters? That won't work!
by Jon B,
Your message is awaiting moderation. Thank you for participating in the discussion.
I have to agree that "static" dirty tracking (such as with an attribute or something) doesn't work in general. For example, what if the setter is implemented like this:
set
{
if (!String.IsNullOrEmpty(value))
{
m_name = value;
}
}
You only want the dirty flag to be set if the m_name is actually changed. Or what happens if you do validation and throw exceptions in the setter. The only way around this that I can see is to do data comparisons to determine the "dirtiness", which has it's own set of (potentially major) issues.
About abstract factory and domain logic
by Zoran Horvat,
Your message is awaiting moderation. Thank you for participating in the discussion.
Regarding Abstract Factory pattern, it hides one effective technique which is rarely used. Domain model may contain if-then-else or switch logic to determine which concrete product to use in further execution. The decision is typically based on some domain data, e.g. create discount if price > 100.
Such domain logic can be moved to a concrete factory like in this article www.codinghelmet.com?path=howto/reduce-cyclomat...
In simpler cases, we can even use lambdas to replace abstract factories like here: www.codinghelmet.com/?path=howto/reduce-cycloma...
The point is that concrete factory then becomes part of the domain model. Its purpose is to wrap creation of one concrete product, but also to act as a kind of strategy. Net result is that domain class can be simplified and doesn't have to contain switching logic anymore.