//the domain object public class Bonus { private DateTime collectedAt; // type can be "EXTRA_CHIPS" or "JAZZ_NIGHT" private final String type; private final long recurrencyIntervalInMilliseconds; public RecurrentBonus(String type, long recurrencyIntervalInMilliseconds) { this.type = type; this.recurrencyIntervalInMilliseconds = recurrencyIntervalInMilliseconds; } public boolean isCollectable() { return DateTime.now() .isAfter(collectedAt.plusMilliseconds(recurrencyIntervalInMilliseconds)); } public void collect() { if (!isCollectable()) { throw new BonusPrematureCollectionAttemptException() } DomainEventDispatcher.dispatch(new BonusCollectedEvent(this)); } } //the persistent object (DTO) in the ACL (insfrastructure layer) @JsonIgnoreProperties(ignoreUnknown=true) public class PersistentBonus implements Serializable { private String type; private long recurrencyIntervalInMilliseconds; private DateTime collectedAt; //getters-setters for each field } //the mapper between the the DO and DTO in the ACL public class BonusPersistenceAssembler { PersistentBonus toPersistence(Bonus bonus) { ... } Bonus toDomain(PersistentBonus bonus) { ... } }
Earlier we have seen that in some specific cases, like rolling release, this separation between our DOs and persistence DTOs comes very handy. Having said that, let's see how the mapping really works.
The trick - encapsulation gets in the way?
The mapping between PersistentBonus and Bonus takes place in the anti-corruption layer. The trick is, since the Bonus, as a well-behaving rich Domain Object, doesn't expose its internal structure, how can we do it at all? No other class knows about its private fields, let alone accessing them. In the followings I explore some possible ways, from trivial and not-so-ideal solutions to some more sophisticated ones.
Solution 1 - Add Getters-Setters to the DO and get over with it
Plainly exposing the internal structure of an object is a major no-no in DDD and OO in general. It violates loose coupling, encapsulation, extendibility, bla-bla. And we've taken pains to hide the internals of Bonus, we shouldn't nullify the effort by making the internals accessible. Forget it if possible.
Using an interface is usually a good idea regardless of our current problem. Let's do that and add an additional Factory class.
public interface Bonus { boolean isCollectable(); void collect(); } public class BonusImpl implements Bonus{ //all the stuff from before // getters-setters } //the only class in the domain directly seeing BonusImpl public class BonusFactory { Bonus createRecurrentBonus(String type, long recurrencyIntervalInMilliseconds) { ... } } // the mapper class in the anti-corruption layer public class BonusPersistenceAssembler { PersistentBonus toPersistence(Bonus bonus) { ... } Bonus toDomain(PersistentBonus bonus) { ... } }
Now we can arrange our code in a way that the other part of the Domain (everything but the BonusFactory) accesses the object strictly through the interface, and the BonusImpl will be only used by the BonusFactory and the BonusPersistenceAssembler. Although the BonusImpl has getters and setters, they are still shielded quite well from the rest of the Domain. I have two minor problems with this solution. One is the necessity of explicit casting from Bonus to BonusImpl in BonusPersistenceAssembler.toPersistence, the other is that at the end of the day refraining from the direct use of the BonusImpl in the Domain is entirely up to the discipline of the developers. In some cases explicit casting of Bonus to BonusImpl might seem the easy way to achieve something and a lazy fellow may fail to resist the temptation. Let's see what can we do about it.
Solution 3 - "Normal" interface + View interface + Factory + package protected implementation class
The first idea is to put the BonusImpl with the BonusFactory in a new package, and change the visibility of the BonusImpl to package protected. Now it's hidden from even the rest of the Domain, but alas, from the BonusPersistenceAssembler, too. Then comes the next idea. Create a new interface, BonusView, which captures only the view of the "data" in Bonus, and let's pass a BonusView to the BonusPersistenceAssembler. Only this class should use the BonusView. In code
public interface Bonus { boolean isCollectable(); void collect(); } public interface BonusView { long getRecurrencyIntervalInMilliseconds(); String getType(); DateTime collectedAt(); } //package protected class BonusImpl implements Bonus, BonusView { //all the stuff from before // getters-setters } //the only class in the domain directly seeing BonusImpl.They are in the same package public class BonusFactory { Bonus createRecurrentBonus(String type, recurrencyIntervalInMilliseconds) { ... } } // the mapper class in the anti-corruption layer public class BonusPersistenceAssembler { PersistentBonus toPersistence(BonusView bonusView) { ... } //uses the BonusFactory Bonus toDomain(PersistentBonus bonus) { ... } }
Better. We still have to cast from Bonus to BonusView before we pass the reference to the BonusPersistenceAssembler, but the ugly getters-setters are nicely confined to a small nook of the code.
Solution 3.1 What about the invariants? - static factory method
However there is still one possible problem to address. If the Bonus has some invariants (not in our simple example, but we are discussing the general idea), having individual setters for each field might be undesirable, since this approach leaves place for putting the object in a state that violates its invariant. In this case in the BonusFactory we can use a static factory method, passing all the fields together (or maybe only the ones that have to be set together to preserve an invariant) to it.
class BonusImpl { public static Bonus reinstantiate(String type, long recurrencyIntervalInMilliseconds, DateTime collectedAt) { ... } // other stuff }
Finally we got rid of the setters. The method is called reinstantiate, suggesting to the developer, that this method is to recreate the DO from something. Conceptually it's very different from simply providing the means to set its fields individually. The drawback is that the factory method will be bloated if the class has more than a couple of fields.
Moral of the story: Setters-getters are troublemakers
It looks like we can tweak the problem in all kinds of smart ways, the result is always a compromise, and we wouldn't need to do anything at all if not for those setters-getters. Let's see how far we have come to mitigate the problem. We started with a simple class and ended up with two additional interfaces, a factory class, a static factory method and package protection. I start to doubt whether it's worth the effort. Let's see a different approach inspired by the static factory method.
Solution 4 - Memento
Instead of passing all the fields to the reinstantiate method, we can create a new class to wrap them up. Let's call it BonusMemento, because it's like a footprint of the Bonus object. It doesn't have any logic, only the data, like the PersistentBonus, just without JSON annotations, Serializable interface and the other technology-related stuff. Unlike the PersistentBonus, the BonusMemento is part of the Domain, even if it's only used to make persistence easier. You can think of it as the skeleton of the Bonus. Just the bones, no brain.
//the original Bonus class, not the interface public class Bonus { public static Bonus reinstantiate(BonusMemento bonusMemento) { ... } // other stuff } public class BonusMemento { private String type; private long recurrencyIntervalInMilliseconds; private DateTime collectedAt; //getters-setters for each field }
No more bloated factory method. No setters either, only getters. But wait! Why can't we use the BonusMemento to replace the getters, too? Instead of exposing its fields one by one through getters, the domain object can be responsible to creating its own Memento.
public class Bonus { public static Bonus reinstantiate(BonusMemento bonusMemento) { ... } public BonusMemento toMemento() { ... } //all the stuff from before } // the mapper class in the anti-corruption layer public class BonusPersistenceAssembler { PersistentBonus toPersistence(BonusMemento bonusMemento) { ... } Bonus toDomain(PersistentBonus bonus) { BonusMemento bonusMemento = convertFrom(bonus); return Bonus.reinstantiate(bonusMemento); } }
Not bad. We are back to the original Bonus class + a static factory method + Memento class. Encapsulation preserved, no bothering compromises. Well, maybe one. The BonusMemento and the PersistentBonus are almost the same, which is a bit of a code duplication. We cannot use PersistentBonus directly in the Bonus class, because the former belongs to the infrastructure layer. But the PersistentBonus can extend BonusMemento, inheriting its content and keeping only the infrastructure-specific part of its former self.
public class BonusMemento { private String type; private long recurrencyIntervalInMilliseconds; private DateTime collectedAt; //getters-setters for each field } @JsonIgnoreProperties(ignoreUnknown=true) public class PersistentBonus extends BonusMemento implements Serializable {} //so what is inside the mapper public class BonusPersistenceAssembler { PersistentBonus toPersistence(BonusMemento Memento) { PersistentBonus ps = new PersistentBonus(); ps.setType(Memento.getType()); //set values for the other fields return ps; } Bonus toDomain(PersistentBonus persistentBonus ) { return Bonus.reinstantiate(persistentBonus ); } }
The toPersistence method of BonusPersistenceAssembler simply copies the content of the Memento to the PersistentBonus field by field. The toDomain is even more simple, since PersistentBonus is a subclass of BonusMemento, we can simply pass it into Bonus.reinstantiate. Awesome.
Solution 5 - Visitor
I've been playing with the idea of using the Visitor pattern to retrieve the values of the fields from the Domain Object.
public class Bonus { public void buildView(BonusVisitor visitor) { visitor.setType(this.type); visitor.setRecurrencyIntervalInMilliseconds(this.recurrencyIntervalInMilliseconds); visitor.setCollectedAt(this.collectedAt); } //all the stuff from before } //in the Domain public interface BonusVisitor { //setters } //in the infrastructure public PersistenceBonusVisitor implements BonusVisitor { private final PersistentBonus persistentBonus; public PersistenceBonusVisitor(PersistentBonus persistentBonus) { this.persistentBonus = persistentBonus } // in the setters call the setters of PersistentBonus }
The visitor pattern is suitable for this kind of situations when we want an object to have control over what it shares about its internals. But at the end of the day we already have it with the Mementos and in a simpler form, so I've decided to stick to that.
Solution 6 - Using reflection
To be frank, I have never tried using reflection. There are some tools available, like Dozer, which promise a painless mapping betweens DOs and DTOs. A colleague of mine has told me about their experiences with Dozer. They could spare themselves the time of writing mapper classes, but they still needed to create the DTOs, the DOs had to have getters-setters, they still wanted to test that the mapping is correct, so couldn't spare writing the tests for the mappers. And performance-wise reflection-based solutions are always heavy. Having said this, I still want to explore Dozer (or something alike) on day.
Summary
In the post we've started from a simple example and explored a couple of solutions for the problem. In the end I would stick to the Memento-one. Basically it's derived from the idea of using a static factory method instead of setters. Since we don't like long parameter-list, we've introduced a new class, the Memento, and then realized, we can use it instead of getters, too. As an additional bonus (no pun intended), we can even derive the persistence class from it. That's it.