Wednesday, April 08, 2009

Terracotta, Domain Driven Design and Anti-Patterns

Yesterday I was going to participate at a technical meeting, when the following statement suddenly appeared on my Twitter timeline:

One interesting reflection is that the "super static" property of Terracotta roots helps us with Domain Driven Design.
Finally we have (what I feel a nice) way of using Repositories in our Entities without Aspects or Register style coding.


My immediate reaction was to reply by saying:

Strongly disagree. That means your business/domain code depends on Terracotta semantics.


I thought my comment were pretty intuitive: if Terracotta is a transparent clustering solution (and it actually is), how can Terracotta help you writing your domain code without polluting the code itself?

Too bad, I was wrong: it generated a lot of discussions with a few of my favorite tech-friends, and what's worse, I wasn't able to further clarify my statement!

Let me blame Twitter 140-characters constraint, please ... I don't want to hurt my own self-respect ;)

So here is a simple-stupid sample, which I hope will clarify the following simple concept: if your code relies on Terracotta super-static variables to access repositories (as apparently stated by the first quote above), then your code is IMO wrong.

Let's go.

We have an Amazon-style application, dealing with a lot of users and books: so we choose to use Terracotta for transparently clustering our application and make it scale.
Taking a deeper look at our domain, and using the Domain Drive Design terminology, we have two aggregate roots: User and Book.


public class User {

private final String id;

// ...
}

public class Book {

private final String isbn;

// ...
}


They're two separated roots because completely independent and with different lyfecycles.
So we have two different repositories, too:


public class UserRepository {

public User findById(String id) {

//...
}

// ...
}

public class BookRepository {

public User findByIsbn(String isbn) { //... }

// ...
}


Now, we want to add the following responsibility to our User: tell us all books he viewed.


public class User {

private final String id;

// ...

public Collection getViewedBooks() { //... }
}


We don't want to establish a permanent relation between User and Book, i.e., make the collection of viewed books an instance variable of user: it would clutter the User entity, and associate it a lot of additional data which is related to the user, but in no way part of the user itself.
So we choose to access Book entities through the BookRepository, meaning that User entities must have access to the repository instance.

The repository is implemented as a collection of objects persisted and distributed by Terracotta, so the following idea comes to our mind: we may configure the repository as a Terracotta root, and access its instance everywhere in our cluster of User objects thanks to its super-static nature!

For those unfamiliar with Terracotta, a Terracotta root, also known as super-static variable, is an object instance shared among all cluster nodes: it is actually created only the first time the new operator is called. After that, that instance will be always the same among all nodes, and every other new invocation will have no effect.

It means that we have a number of super-easy ways to access our super-static repository from inside our User entity, and here is one:


public class User {

private static final BookRepository bookRepository =
new BookRepository();

private final String id;

// ...

public Collection getViewedBooks() {
for (String isbn : viewed) {
Book book = bookRepository.findByIsbn(isbn);
}
// ...
}
}


Thanks to Terracotta, the BookRepository will be actually created for the first time, and then always reused, even if we shutdown our application and restart: the bookRepository persisted/distributed state will be always there.

Well, this is IMHO an anti-pattern.

While it is true that Terracotta makes easy to access repositories from entities, it is IMO wrong because coupled to Terracotta itself: if you swap out Terracotta, that static variable will be always reset at every shutdown/restart, as you would expect in any Java application, and your data will be gone!

I hope it's clearer now.
And if someone disagrees, or just thinks I misled the original quote, I'd be more than happy to hear his thoughts.

Finally, a disclaimer.
I'm an happy Terracotta user, and there are many ways to correctly use Terracotta in your application: putting it simple, just hide the way it works behind the repository implementation, and access the repository itself as it were a normal Java object, not a super-static one.

And that's all.

10 comments:

Unknown said...

First of all, the following code snippet that you presented is POJO based - there is no external dependency irrespective of whether we use Terracotta or not. We have implemented a repository which does the fetching of the viewed books. The additional semantics that Terracotta offers is that of clustering and persistence - both of which I feel, are concerns orthogonal to the primary domain model. There is nothing in the code snippet below which makes the code fail as per the contract which it publishes. Now, if I want to add the transparent clustering feature of Terracotta, I spruce up some configuration (external) and deploy the code in the Terracotta container. The domain model remains the same, except that it now has an additional capability to persist across system restarts.

Now let me change the implementation a bit and make the repository injectable. Then I can inject any implementation through DI in my entity class (or better in a service class). If I am using Terracotta then the normal memory based implementation will be injected (as above). Otherwise I can choose to inject an implementation which is backed directly by an RDBMS that offers failover support (e.g. Oracle RAC). In fact we did the same thing for one of our customers where the repositories were made fail safe through a combination of Oracle RAC (at the database level) and AOP (at the application level).

But my point is, whatever be the persistence or clustering support, it is a concern which is orthogonal to the domain model. The domain model remains the same irrespective of whether we choose to implement failovers using Terracotta.

Cheers.

Sergio Bossa said...

Let me quote myself:

"There are many ways to correctly use Terracotta in your application: putting it simple, just hide the way it works behind the repository implementation, and access the repository itself as it were a normal Java object, not a super-static one."

Then your comment:

"Now let me change the implementation a bit and make the repository injectable. Then I can inject any implementation through DI in my entity class (or better in a service class). If I am using Terracotta then the normal memory based implementation will be injected (as above). Otherwise I can choose to inject an implementation which is backed directly by an RDBMS"

Aren't we stating the same? :)

Hide Terracotta behind an injected/looked-up repository implementation.
If you do so, super-static variables will not play any role in the movie, and everything will be ok.

Taylor said...

@debasish I am with Sergio on this one. There is a semantic dependency on Terracotta in his example, his point is that without Terracotta, the example doesn't work as expected - certainly it works, but it doesn't provide the expected results.

If, on the other hand, the persistence is abstracted into the BookRepository, such that the Terracotta semantic dependency is resolved there, you could easily swap out Terracotta persistence and clustering with EJB 3.0 annotations and the like, and no one would be the wiser.

I think that is Sergio's point.

@sergio - small nitpick about the post - it's clear to me that you are intentionally describing an anti-pattern in this post - do you think you could edit the post somewhere and spell that out unambiguously with something such as

WARNING - ANTI-PATTERN

I wouldn't want to confuse anyone! :)

Great post by the way!

Taylor said...

@debasish btw your resolution to Sergio's problem is to use DI to inject differing Repository implementations - I agree that this would also resolve the issue appropriately.

Sergio Bossa said...

Thanks, Taylor.
Your words make perfect sense.

I modified the post to make it clearer it's IMO an anti-pattern.

Cheers,

Sergio B.

Peter Backlund said...

You should look into the value object pattern and how to make concepts from the ubiquitous language explicit. ISBN would be a great target for this.

Take a look at the DDDSample application, specifically the UnLocode class, which is a value object that encapsulates domain knowledge and raises the type level form the low-level String to the domain-specific UnLocode. It makes your code more readable and intention-revealing.

Stefan Norberg said...

Hi,
Just what to trace back the quote to the source for you - Jonas Bonér Twittered the quote from an email to myself and some other folks authoered by Jonas Gustafsson, Sportsbook Architect at Unibet.

/Stefan


------ Forwarded Message
From: Jonas Gustafsson Date: Fri, 27 Mar 2009 11:55:44 +0100
To: Stefan Norberg
Subject: Brief plan for Sportsbook and Terracotta

[snip]
Phase two:

All live non user specific content is stored and managed with Terracotta (BetOffers, Events, Criterias), with async write to the database. In phase one a generic domain repository was implemented, which is going to be used as a base for all repositories. On interesting reflection is that the "super static" property of Terracotta managed roots helps us with Domain Driven Design. Finally we have (what I feel a nice) way of using Repositories in our Entities without Aspects or Register style coding.

[snip]

Stefan Norberg said...

See http://pastebin.com/f42e153fa

Sergio Bossa said...

Hi Stefan,

thanks for pointing back to the actual source.
Regarding your little code snippet, may you elaborate more?

Thanks,
Cheers,

Sergio B.

Jonas Gustafsson said...

Hi,
As author of the quote that started a quite fun and interesting discussion, I would like to but it even more in the right context. First of all it was a first reflection of Terracotta so I really haven’t stayed up to many nights thinking of this.
I see some problems with your implementation above, but I don’t see any problem with code depending on Terracotta. Without Terracotta and one instance (and one classloader) this will work just fine. IMHO this is not depending on Terracotta. Regardless that it have other problems regarding the ability to test the code and the hard coupling. Preferable (as said by Debasish said) the repository should be DI or possible to change thru look up. The main problem with this is that it goes against my first quote – that Terracotta will help us with DDD without aspects.
To make it short I find it hard to use DDD because of the complexity to inject Repositories in our Aggregate roots/entities. Aspects works fine but is too complex for an average development department. (You need 4 blue and 5 red developers, but you get 3 blue and 4 green painted red :)
Still think it can simplify DDD because of the POJO nature of the Repositories, but as a disclaimer I still need some nights thinking of this :). Of course a POJO based repository is going to be dependent on Terracotta (to get it useful), but with the semantics from the JVM. The code is anyway always going to be dependent on the underlying persistent solution. Regardless using JPA, Terracotta, etc you need to make your code so that your underlying persistent solution is going to perform. Using Repositories or DAO:s without respect to batching, transaction etc. is never going to perform.

/Jonas