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.