Here is a snippet of the problem as described by the author of the mail, Pascal:
I'm in a situation where we are redesigning a large core business system using domain driven techniques (which we've come to love, BTW). The resulting domain model is to be used by several different applications on the internet and intranet, each with their own restrictions. For this we'll use application layers to disclose application-specific business operations. Along with that, we will need application specific views on the domain model. Now we're faced with the challenge to create these views efficiently.
I know some say: just have the application layer pass the domain model to the outside world. This seems the simplest, but doesn't work for several conceptual and practical reasons. The most important conceptual reason is that the data to be passed outside is to be restricted. For example, in the domain model a Customer can have a 1-to-1 relationship with an Address, but we don't want the internet application to have access to the Address. In that case we cannot expose a Customer domain class.
Let's elaborate a bit about this.
One straight solution is making a copy of the domain object, populating only desired properties, while setting null protected ones.
This is a sort of workaround, and I will not go further.
Another solution is obviously the use of Data Transfer Objects.
You can construct specialized DTOs containing only non-protected properties and use them in your views.
Even if you don't think at DTOs as an anti-pattern, and even if you are not an OO purist, DTOs have at least two serious problems: they duplicate code and have big maintenance problems.
A third solution could be the use of the proxy pattern : specifically, a protection proxy.
A protection proxy is simply a proxy for controlling access to object methods.
We'll use dynamic proxies, that are proxies created at runtime: specifically, we'll use Java dynamic proxy APIs.
I assume that you already know ho to use Java dynamic proxies: if not so, take a look here.
Let us recall the problem to solve: we want to forbid access to some object methods, in particular some setter and getter methods.
Using proxies, we could solve this in two ways:
- Creating a proxy which implements the domain object interface and throws an exception for protected methods.
- Creating an adapter proxy which implements a restricted interface without the protected methods.
Throwing an exception where clients expect normal behavior is not so good: it is like violating the interface contract.
At a glance I would not choose this.
However, after more thinking and crunching, this technique is widely used in Java APIs, and permit us to switch from the original domain object to the proxied one without changing client code, because they both implement the same interface: if the protection proxy is well documented, I think this is a good compromise.
So, let us implement our protection proxy which will throw an exception for protected methods.
First, a simple domain object with its interface:
public interface ICustomer {
public String getName();
public void setName(String name);
public String getPassword();
public void setPassword(String password);
}
public class Customer implements ICustomer {
private String name;
private String password;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
We want to forbid access to getPassword and setPassword.
Here is an InvocationHandler which will do the job:
Names of the methods to protect are hard coded: this is an example, and you can make a more flexible proxy.import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class CustomerProtectionProxy
implements InvocationHandler {
private ICustomer student;
public CustomerProtectionProxy(ICustomer student) {
this.student = student;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if (method.getName().equals("getPassword") ||
method.getName().equals("setPassword")) {
throw new IllegalAccessException();
}
else {
return method.invoke(student, args);
}
}
}
Then, a simple factory for proxying domain objects:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class SimpleProxyFactory {
public static Object makeProxy(Object target,
InvocationHandler handler) {
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
handler);
}
}
And finally, a JUnit test case:
import junit.framework.*;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class SimpleProxyFactoryTest extends TestCase {
public SimpleProxyFactoryTest(String testName) {
super(testName);
}
public static Test suite() {
TestSuite suite = new TestSuite(SimpleProxyFactoryTest.class);
return suite;
}
public void testMakeProxy() {
ICustomer customer = new Customer();
customer.setName("Mark Red");
customer.setPassword("impossible");
ICustomer protectedCustomer =
(ICustomer) SimpleProxyFactory.makeProxy(customer,
new CustomerProtectionProxy(customer));
protectedCustomer.setName(protectedCustomer.getName());
try {
protectedCustomer.setPassword("new");
fail("You should not be able to call setPassword");
}
catch(Exception ex) {
}
try {
protectedCustomer.getPassword();
fail("You should not be able to call getPassword");
}
catch(Exception ex) {
}
}
}
In this way, protecting a domain object is only a matter of writing a proxy and applying it through a simple factory.
Note that writing a dynamic proxy is a lot less expensive than writing DTOs, because you don't have to duplicate every attribute and every getter/setter method: you only deal with protected methods.
Moreover, you have the extra gain of being able to switch from the original domain object to the proxied one, and vice versa.
One final word: DTOs are an open debate and I'm not saying, in any way, that this is the best solution.
There are surely a lot of scenarios where other solutions are more viable: I'm just saying that protection proxies are a good solution which can avoid hand made DTOs.
And I'd like to know your feedback.
11 comments:
It looks like a quite interesting approach, however: It would be even better if you explored not having to hardcode the protection quite so hard. It would also be interesting to actually measure the performance of the solution, compared to DTOs. Like you say in your post, I've also found dynamic proxies to be surprisingly inexpensive (that is, if it is performance and not productivity you're talking about). But it would be good to know: How fast.
Lastly, claiming "protection proxies are a good solution which can avoid hand made DTOs" is maybe a bit of an overstatement. The original intent of DTOs is not protection, but transfer, an aspect that protection proxies does not address at all. :-)
Still, it seems like a promising approach.
Sergio,
I can see how this would be a very workable solution when you want to publish restrictive interfaces. From that point of view it looks very interesting.
However, I have to agree with Johannes: it does not solve the problem of transfering only a restricted set of data (which I called a restrictive view). In my case, the (coarse grained) service layer should simply not pass out any of the restricted data. In such a case, I'm afraid we're still at the mercy of DTO's.
Pascal Lindelauf.
I believe your approach to using proxy is viable as long as you don't need to present the information in a different way. Let me give a brief example of what I'm talking about:
Suppose I have a Asset object that has a getAssetValue() that performs a expensive calculation, (using other objects, services and/or repositories). If I'm using the proxy pattern I'll be unable to cache this value and assign it to a view Object (call it DTO or adapter) speeding up my application.
I want to stress out that I don't favor DTO because of the maintenance nightmare, but I believe there is no silver bullet in this issue.
Johannes Brodwall said...
> It looks like a quite interesting
> approach, however:
> It would be even
> better if you explored not having
> to
> hardcode the protection quite so
> hard.
Yes, I hope to explore this in another post or comment here.
> Like you say in your post, I've
> also found dynamic proxies to be
> surprisingly inexpensive (that is,
> if it is performance and not
> productivity you're talking about).
> But it would be good to know: How
> fast.
I was talking about productivity, but with modern JREs they should have good performance ... I'll post two simple tests for measuring time in milliseconds ...
> Lastly, claiming "protection
> proxies are a good solution which
> can avoid hand made DTOs" is maybe
> a bit of an overstatement.
> The original intent of DTOs is not
> protection, but transfer, an aspect
> that protection proxies does not
> address at all. :-)
Yes, you are right, I was referring only to DTOs as protection objects ... protection proxies are surely not a replacement for transfer objects ;)
Thanks for your feedback!
Sergio B.
Pascal Lindelauf said ...
> I have to agree with Johannes: it
> does not solve the problem of
> transfering only a restricted set
> of
> data (which I called a restrictive
> view).
Why do you put such an emphasis on "transferring"?
Why a proxy is not a viable solution for your problem?
Maybe I'm missing the true point of your problem ...
> In my case, the (coarse
> grained) service layer should
> simply
> not pass out any of the restricted
> data. In such a case, I'm afraid
> we're still at the mercy of DTO's.
If your needs are explictly to TRANSFER objects, be aware: DTO are AFAIK the ONLY (and EFFECTIVE) way to go.
They are an anti-pattern if you use them for mapping domain objects to views objects!
But if you need to transfer data, whatever solution you try you'll always end up (IMHO) with some sort of DTO.
My best regards,
Sergio B.
Roni Burd said ...
> Suppose I have a Asset object that
> has a getAssetValue() that performs
> a expensive calculation, (using
> other objects, services and/or
> repositories). If I'm using the
> proxy pattern I'll be unable to
> cache this value and assign it to a
> view Object (call it DTO or
> adapter)
> speeding up my application.
Be aware : you are addressing another problem not related to protection proxies.
If you elaborate more about this, maybe in your blog, we can try to discuss a solution ...
My best regards,
Sergio B.
>
> If your needs are explictly to TRANSFER
> objects, be aware: DTO are AFAIK the ONLY
> (and EFFECTIVE) way to go.
>
> They are an anti-pattern if you use them
> for mapping domain objects to views objects!
> But if you need to transfer data,
> whatever solution you try you'll always
> end up (IMHO) with some sort of DTO.
>
What you could implement is some kind of generic DTO that allows you to use the same DTO for many domain objects. This would at least remove some of the duplication. Basically, a combination of a HashMap and commons-beanutils should do the trick.
I am not sure how smart it is, though...
Sergio Bossa said...
> Why do you put such an emphasis
> on "transferring"?
> Why a proxy is not a viable
> solution for your problem?
> Maybe I'm missing the true point
> of your problem ...
The problem is that the tier that is using the domain model is a service tier, that exposes coarse grained functionality to the outside world (e.g. through webservices) and, along with that, coarse grained data structures. So you'd typically have a getPersonDetails() instead of getFirstName(), getLastName(), etc.
After some discussions on the domain driven design forum and further thinking, I've come to realize more clearly that: the service layer is the client (or "end-consumer" if you like) of the domain model. The difference with the case where the client is a UI layer, is that in the first case, the domain model is mapped onto the data structures offered to the outside world by the service layer, whereas in the latter case, the domain model is mapped onto the data structures that make up the UI. Thus, the UI-layer data structures are screens/forms. The service layer analogy of the concept of screens/forms is then the concept of DTO's.
My original problem was stated more from a view as consumer of the services offered by the service layer.
I hope this puts things a little in perspective.
Cheers,
Pascal Lindelauf.
Pascal said ...
> The problem is that the tier that
> is
> using the domain model is a service
> tier, that exposes coarse grained
> functionality to the outside world
> (e.g. through webservices)
The key point is now clear : you need to expose your objects to the outside world.
If so, proxies are not a solution and I think you cannot avoid DTOs.
I say more: I think DTOs are the RIGHT solution.
Do you agree?
Or are you looking for another way?
Regards,
Sergio B.
Sergio Bossa said...
> The key point is now clear : you need
> to expose your objects to the outside world.
> If so, proxies are not a solution and I
> think you cannot avoid DTOs.
> I say more: I think DTOs are the RIGHT solution.
Like you I still don't see any other way of solving this than with DTO's. In the section "Service Layer" of Fowler's PoEAA, Randy Stafford aptly states: "It likely entails a lot of extra work to make your Service Layer method signatures deal in Data Transfer Objects", but follows with the suggestion: "My advice is to start with a locally invocable Service Layer whose method signatures deal in domain objects.". As the latter would in theory indeed save a lot of work, problems arise when you don't want the entire domain object graph to be communicated by the service layer; in other words: when we want a more restrictive view. I'm currently thinking that Randy's advice is not executable with a restrictive view requirement, but I'm still hoping someone can prove me wrong.
Pascal.
Pascal,
I think when Randy Stafford was referring to services, he didn't actually mean "Web Services".
In fact he states in the following paragraphs that as soon as you require remoting (which is the case with web services), then it's better to create a Remote Facade and go for DTOs.
I don't think he meant that exposing domain objects remotely was the right approach, his advice is only limited to the co-located approach.
Post a Comment