Inversion of control domain objects construction problem
As I understand IoC-container is helpful in creation of application-level objects like services and factories. But domain-level objects should be created manually. Spring's manual tells us: "Typically one does not configure fine-grained domain objects in the container, because it is usually the responsibility of 开发者_JAVA百科DAOs and business logic to create/load domain objects."
Well. But what if my domain "fine-grained" object depends on some application-level object. For example I have an UserViewer(User user, UserConstants constants) class. There user is domain object which cannot be injected, but UserViewer also needs UserConstants which is high-level object injected by IoC-container.
I want to inject UserConstants from the IoC-container, but I also need a transient runtime parameter User here.
What is wrong with the design?
Thanks in advance!
UPDATE
It seems I was not precise enough with my question. What I really need is an example how to do this:
create instance of class UserViewer(User user, UserService service), where user is passed as the parameter and service is injected from IoC.
If I inject UserViewer viewer then how do I pass user to it?
If I create UserViewer viewer manually then how do I pass service to it?
there's nothing wrong with this design. you use Factories
for that, which have one leg in the domain, one leg in infrastructure.
You can either write them manually, or have the container do that for you, by things like TypedFactoryFacility in Windsor.
Also when your domain objects come from persistence layer you can plug your container there to inject the services they require (NHibernate can do that).
But what if my domain "fine-grained" object depends on some application-level object?
It is precisely this that is considered bad-practice. I would say the problems could be:
- There are tons of these objects, so there can be performance and memory issues.
The POJO style is that they can be used in all environments (persisted in the database, processed in business algorithms and rules, read and set in view technologies, serialized and send over the network). Injecting application-level objects in them could cause the following problems:
- In your architecture, you probably have the rule that some (most) application-level objects are usable in some layers, not in others. Because all layers have access to the pojos, the rule would be violated transitively.
- When serialized and rebuild in another JVM, what would be the meaning of your application-level objects. They are useless, they must be changed for the local equivalents...
Typically, the pojos that constitute your domain are self-contained. They can have access to other pojos (and many enums), that's all.
In addition to the data, they have methods that implement the details of the business rules or algorithms (remember the OO idea of grouping data and code that work on it ;-) ):
- This is especially good when they have inheritance, as this allow to customize a business rule for some pojo by providing a different implementation (differing case without if or switch: remember OO? ;-) ).
- Any code that requires access to application-level objects (like accessing the database) is taken out, for example to a Service or Manager. But that code stays high level, thus readable and simple, because the pojos themselves take care of the low level details (and the special cases).
After the fact, you often find out that the pojo methods get reused a lot, and composed in different ways by the Services or Managers. That's a big win on reducing duplication, the methods names provide much needed "meaning", and provide an easier access to developers that are new to a module.
For your update:
create instance of class UserViewer(User user, UserService service), where user is passed as the parameter and service is injected from IoC.
If I inject UserViewer viewer then how do I pass user to it?
If I create UserViewer viewer manually then how do I pass service to it?
In that case, you need a factory method (possibly on a Factory or Locator of yours). It could look at follow, separating the two parts:
public UserViewer createUserViewer(User user) {
UserViewer viewer = instantiateBean(UserViewer.class);
viewer.setUser(user);
return viewer;
}
private <E> E instantiateBean(Class<E> clazz) {
// call the IoC container to create and inject a bean
}
精彩评论