I have been getting some great help over at the ORM forum, and although this is related to that question in a sense, it is more about the relationship between the domain model and the other layers in a system, hence posting here.
I have been reading Ch3 of Expert Spring MVC and Web Flow, which i found as a pdf online, this along with the help from the ORM forum has opened my eyes to how much I understood, and what I didnt understand about modern design practice, but I still have the following confusion:
If, a domain model is to have business logic. How does it manage to achieve being decoupled from the either the service layer or the persistance layer.
If we have an Author object with a create method, at some point that create method is going to need to persist the new user, now as I understand it, creating the Author is the job of the service layer, as it is a corase grained UseCase (eek bit of buzz word bingo for a friday lunch time).
So just what does the Author.create() do, does teh service layer call this as part of its responsiability, and if it does, isint the method going to make use of a persistance layer DAO to actual do the persistance, and in so doing dosnt that expose a dependancy/coupling to the persistance layer?
Just having trouble in how to decouple my domain model from the persistance layer, currently my Domain Model is just DataObjects, then I have a BO layer (~Manager's), but even that has a dependancy on a DAO from the persistance layer, so mergin the DataObject with the BO object will cause a dependancy between the doaminmodel and persistance layers.
Perhaps Author.create(), isnt part of behavior of the Author. But then what is? publishing a book? but even that would requrie some form of interaction with the service/persistance layer and strikes me more of a use case that should be in the service layer.
If, a domain model is to have business logic. How does it manage to achieve being decoupled from the either the service layer or the persistence layer.
Primarily through repositories, sometimes through services.
From: Eric Evans: Domain Driven Design � Tackling Complexity in the Heart of Software (amazon US)
MODEL-DRIVEN DESIGN - A design in which some subset of software elements corresponds closely to the elements of the model. Also, a process of codeveloping a model and an implementation that stay align with one another. SERVICE - An operation offered as an interface that stands alone in the model, with no encapsulated state. REPOSITORY - A mechanism for encapsulating storage, retrieval, and search behavior which emulates a collection of objects. ENTITY � An object fundamentally designed not by its attributes, but by a thread of continuity and identity. VALUE OBJECT � An object that describes some characteristic or attribute but carries no concept of identity. AGGREGATE - A cluster of associated objects that are treated as a unit for the purpose of data changes. External references are restricted to one member of the AGGREGATE designated as the root. A set of consistency rules applies within the AGGREGATE's boundaries. FACTORY - A mechanism for encapsulating complex creation logic and abstracting the type of created object for the sake of the client.
Domain Driven Design Homepage
There is also a 'free' (in exchange for your eMail address) primer available: Domain Driven Design Quickly
So just what does the Author.create() do, does the service layer call this as part of its responsibility, and if it does, isint the method going to make use of a persistence layer DAO to actual do the persistence, and in so doing doesn�t that expose a dependancy/coupling to the persistence layer?
It is the Author's responsibility to to write books (in this case the author is likely the managing container of the books he writes (what about books with multiple authors?)). If the author was also supposed to create other authors you would be violating the Single Responsibility Principle (SRP). Using constructor based creation is usually only appropriate for VALUE OBJECTs. For ENTITY objects you wouldn't be able to track their thread of continuity and identity as easily if you used constructor based creation. For more complicated objects there is also the matter of internal invariants. By designing constructors carefully you could design them to guarantee their internal invariants - however many frameworks require the presence of default constructors, property style getters and setters usually making it more difficult to enforce internal invariants. You could place a static factory method at your base class but that can introduce cycles as the base class would need to know about its subclasses - that would violate the Acyclic-Dependencies Principle (ADP). Also in many cases you are dealing with AGGREGATES rather than ENTITIES, and AGGREGATES typically require a FACTORY for their creation. So often the creation method is placed on the REPOSITORY which delegates to a FACTORY for AGGREGATES. The REPOSITORY will ultimately use the persistence layer (DAOs/ORM) - however it also choreographs and delegates the assembly and disassembly process to FACTORIES and other helper classes - in the case of an ORM much of the assembly and disassembly process would be accomplished through the mapping configuration.
[ April 20, 2007: Message edited by: Peer Reynders ]
Here's one model that might be ok. The "V" are down facing arrow heads ...
Does your picture have any arrows running in bad directions? You can sometimes reverse an arrow by moving an interface from one box to the other. Sometimes it's trickier. Let us know if you have any specific issues; maybe we can help turn them around.
Robert Martin's book "Agile Software Development" aka Patterns, Principles and Practices is a great resource for this kind of thing. Tools like JDepends can analyze code to help you draw the pictures.
Let me trying and draw an example of the problem I am having:
The need to create a new user for the application
What I currently have.
the V are down arrows
Now, All the layers depend on the User object as they either display it or set it(UI)/(WEB), create/get it (Service).
Now from what I understand, and this is my confussion, User should have a createUser method, something like:
[B]My understanding of what I should have[B]
the V are down arrows
The userManager uses the createUser method of the User object to persist the newly created user.
Which introduces a dependacy between the Domain Model and the application persistance layer, which strikes me as bad.
So I guess the question is:
Is createUser part of the user Domain, or is it a service offered by the applciation? Currently I think it is a service.
On the other hand I can see it is quite elegant that service layer uses the DomainObject to do its "tasks", the service layer not actual having to know anything about the object it is working on, but then that begs the question why do I need a service.
Thank you and Sorry
[ April 24, 2007: Message edited by: Gavin Tranter ]
Originally posted by Gavin Tranter:
User should have a createUser method
Where did this requirement come from? What is wrong with:
Your user is now created. If it has a user id that ID would be 0 - the only identity that this user currently has is as this object. So the next step is to register the user object.
Now the user object is registered with the current Unit of Work (basically a built up transaction) and its inserted into the Identity Map of the repository - so the user object is "queued up" for insertion into the persistence layer and the repository is "aware" of the existance of the new user object. To persist everything you:
Nilsson refers to the persistence abstraction layer as a "Workspace". All domain repositories sit on top of that "workspace" which can be either your ORM framework, a fa�ade on top of your ORM framework (better for TDD), or a fa�ade on top of your Table Data Gateways (DAOs).
If you are uncomfortable with the zero ID then you can combine steps 1 and 2:
I like Peer's ideas - a couple good choices on creating new Customers. We could show a business layer facade in place of the repo with either signature - or maybe that's what userRepo was.
In a lot of web architectures the servlet container doesn't touch real domain objects. Instead it works with Data Transfer Objects, DTOs. These can be custom made to map to the forms on the UI so a get from the facade returns exactly what's needed on the screen, and an update sends exactly the fields that were modified on screen. I'm not real wild about this kind of thing but it does get those model dependencies out of the UI.
Some persistence tools can also break the dependency on the model because they work with any JavaBean or any implementation of some persistable interface. I've worked most recently with persistors that know about the model because that total automation is hard to get right.
Peer, its not a requirement, it has come from: "domain objects should encapsulate behaviour not just state".
Both the ORM thread (I started) and several other reads all agreed that a domain object should encapsulate its behaviour, and it looks like I managed to get myself confused over just what behaviour belongs in a domain object and what is a service offered by the application.
You have both helped to clear this up. I think what I have is "correct", in as far as it goes.
Your steps look some what like how I am approaching the situation:
1) fill out form values ----- UI
2) new user from form values ----- Web
3) userManager.createUser(user) ----- Service
4) userDao.Insert(user) ----- Persistence
I think I am using Table Gateways, I have a "userManager" in the Service layer that basically encapsulates anything I may wish to do with a user object.
I then have some DAO, and DAO implementations in the persistence layer which is currently straight JDBC calls.
The web layer is just servlets controlling what is displayed, while the UI layer is JSP doing the rendering, I am trying to have as little application logic as possible in the UI, I am aiming for it being simply a view with all the logic in the underlying layers (web/service/persistence).
My Domain objects are just basic JavaBeans with getters and setters for the objects properties. As I learn more, and ask more "dumb" questions, I expect their reasonability to change.
I think perhaps my service layer is a workspace, but I have no concept of unit of work, or an identity map. I think these will come later when I add ORM.
Also I think it will be interesting to see just how the architecture I have put in place will handle the replacement of the JDBC DAO with the ORM implementation, see just how decoupled I managed to get things.
I find it very hard to "learn" something from a book (you cant ask it "why?"), so thank you, you have been very patient with me, and helped a great deal. I shall be keeping these threads for reference and further reading.
Originally posted by Gavin Tranter:
agreed that a domain object should encapsulate its behaviour
In this particular case the user is there 'to be user' - I suspect that in your domain it will actually represent the user's identification and authorization within the application. Lets say you have some rather detailed security requirements: Your user could belong to multiple user groups each of which contribute a different set of access privileges within your application - carrying those groups and privileges is the responsibility of the user object - and any specialized behaviour that may emerge from that information.
Creational behavior is often factored out of single domain classes and put into factories, especially as you are always trying to identify AGGREGATEs in your domain model.
For the 'more complicated version of the user' the UserRepository would have potentially hit the workspace three times to materialize a user:
If the inter-relationships of the parts are complicated enough, the repository would delegate the re-materialization of the user to a UserFactory.
3) userManager.createUser(user) ----- Service
In Domain Model speak your userManager is actually a REPOSITORY. A SERVICE has three characteristics:
A contrived example with the 'complicated user': lets say there is an area in the application that needs a special access token that may only be granted if you have one of a variety of sets of access privileges. You may have a very good reason for NOT wanting to include this 'behaviour' (it's too specialized) in your user class - then you can simply move it into a SERVICE. Normally a service wouldn't be anything that simple - in many cases it has to look at a number of different domain object instances and access some external resource(s) via the workspace.
My Domain objects are just basic JavaBeans with getters and setters for the objects properties.
Well for what you have described so far a Domain Model is overkill. The original thread mainly started as an attempt to convince you that investing some time into learning Hibernate (as an ORM) is worthwhile as it would save you the hand-coding of DAOs and give you the option to refactor later towards a Domain Model when the application got a bit more complicated. (I personally don't see any value of putting DAOs on top of Hibernate as it is shown in a number of articles - once you have an ORM you should be designing repositories, not DAOs).
However a simple example makes things easier to explain - but at the same time it doesn't necessarily highlight the benefits (e.g. when the repository only accesses one Table Gateway behind the scenes.)
I have no concept of unit of work, or an identity map
Think of the 'unit of work' as a transaction. By beginning the transaction, you start a new 'unit of work'. Once you commit the transaction you are persisting all the changes that have been made to the 'unit of work' since it was created. If you ROLLBACK you are disposing of the 'unit of work' and none of the changes are saved.
The 'identity map' is a way for to keep track of the object instances that are currently in memory (because these are the most up to date versions). When you start your unit of work your identity map is typically empty. While the unit of work is in progress any objects that are retrieved from the workspace are added to the identity map (as well as any new ones). So before retrieving any objects the 'identity map' is searched first before any call is made to the persistence layer (which may not be needed if the object is found). Usually the identity map is cleared once the unit of work is committed or rolled back.
Repositories based on an ORM workspace usually do not have to implement the unit of work and identity map because the ORM does it for them - however when you create a 'fake workspace' for TDD you will have to throw in a simple construct that mimics the unit of work and identity map so that the 'fake workspace' behaves as expected.
[ April 25, 2007: Message edited by: Peer Reynders ]
The reason I have not done so yet, is that I am still battling a little with Spring MVC, and was having this confusing over the dependancy between layers.
I dont want to be fighting with Hibernate while still fighting with both the concepts and while still try to solve some of the Spring issues I have.
I wont get it right, you never do, not first time anyway, but I hope I will have a cleaner solution which isint getting in the way of teh complex DB/Domain model I have managed to create for myself.
Thanks, owe you guys a pint