IMHO: DDD is NA to SOA, but OK for OOP…

Posted: May 22, 2011 in architecture
Tags: , , ,

Perhaps I’ve had a bad week, but I am not a fan of DDD… today. Maybe it’s because I’ve spent close to 50% of my career refactoring code that was over-engineered, unclear, or redundant… and DDD just happened to be present. Maybe the previous generation of programmers sat around the water cooler discussing design patterns, and they agreed DDD was second best only to pre-sliced pancakes. Maybe I haven’t really taken the time to understand when DDD really works, and that my sorrows and anger are misplaced. Perhaps many more agree with me, and I could incite lively discussion on DZone or the comments below…

I think it was my uncle who I first heard the phrase uttered, “Always walk a mile in the other guy’s shoes… so you’re a mile away and you’ve got his shoes.” Lets start with what DDD is, a simple example, and some of the pitfalls I’ve had to deal with this week.

DDD is Domain Driven Design. In a nutshell, the idea is to take the Object Oriented Programming (OOP) principles, understand them, then make them pervasive in your application architecture. The original concept of OOP was to bundle data, and logic that guards said data, together in a structure called an Object. The theory is that you model Object after real-world behavior, or a popular buzzword these days is “business logic.” Lets put the business logic right with the business data and pass it around. This is very OO! Eventually you’re so OO, that your ‘view layer’ applications are essentially just reflections of your domain layer… and actually DDD an entire process, but I’m going to just focus on the coding part for this post…

A good start would be the old, oft-abused Bank Account example. Lets have an object that represents someone’s bank account:

public class Account {
 private long balance;  // bitcoins
}

This has data, but no business logic, so we can’t get to their account balance. Lets add some:

public class Account {
 private long balance;

 public long getBalance(){
  return balance;
 }

 public void decrementAmount(long amount){
  balance = balance - amount;
 }

 public void addAmount(long amount){
  balance = balance + amount;
 }
}

Well this isn’t bad. I can clearly see what’s going on here; the methods are self-describing, and the code is clear about its intentions. I’m really feeling good about this actually.

public class Account {
	private long balance;

	public long getBalance() {
		return balance;
	}

	public void decrementAmount(long amount) {
		balance = balance - amount;
	}

	public void addAmount(long amount) {
		balance = balance + amount;
	}

	public void transferAmountFromAccount(long amount, Account foreignAccount) {
		foreignAccount.decrementAmount(amount);
		addAmount(amount);
	}

	public void fireInterestCaculation(double interestRate) {
		// office space arithmetic
		long interest = (long) (interestRate * balance);
		addAmount(interest);
	}
}

Well this is interesting… In accordance with the DRY principle, the transfer and fireInterest methods begin using other public methods internally. We could continue, maybe throw exceptions for NonSufficientFunds by calling getBalance first, or maybe even make fireInterest a recursive calculation. The reuse gets even better once we get to polymorph, extend, and generalize our objects. As we use the OO principles, we continually invoke the information hiding principle and complex business logic becomes nothing more than a glorious stack of method calls… And by now you too are thinking such a method stack could only be rivaled by a stack of pre-sliced pancakes.

So what is the opposite of DDD? An ‘Anemic Domain Model’, or one which domain object carry data but no logic. Here is the equivalent object in an anemic model:

public class Account {
	private long balance;

	public long getBalance() {
		return balance;
	}

	public void setBalance(long balance) {
		this.balance = balance;
	}

*Business logic is in a service somewhere…

Let me be perfectly honest… I started this article very harshly. I actually like DDD. DDD is the manifesting of all OO principles. You’re using the language to its fullest extent. Design patterns flow more naturally. Maybe you have fully achieved the purity of quantified conception… I hear this repeated, “Anemic domain models are leftover from procedural programming,” which to some extent is true. But are they all the bad? No, but with an Anemic Domain Model you aren’t taking advantage of the language. So DDD is the way to go? I think so, but there seems to be a few fundamental limitations with DDD that I think must be managed, and in my career, I’ve seen them managed ineffectively. The articles I’ve read on DDD focus very much on the benefits of DDD and how to implement it, but fair miserably at managing the lifecycle of DDD.

Jon’s DDD Antipattern #1: Your programmers must be domain experts.
This is an oft-omitted detail of doing DDD. Writing DDD effectively means you need to have programmers that understand the language of your domain fully, or, your analysts (that understand the domain fully) must be able to communicate in terms of OO. This should be fairly obvious because effective DDD means translating your domain from terms of accounts and interest rates to objects and functions. Furthermore, if your organization has a lot of turnover, you will be bitten rather than benefit from DDD as knowledge of how your domain was implemented evaporates.

Solution: Retain your experts, both technical and business. Recruit fresh talent into the organization to widen the pool. Group your domain experts and technical staff by domain, not by job position.

Jon’s DDD Antipattern #2: The impatient incompetent programmer
Impatient and lazy programmers are general antipatterns, but I feel in DDD they can really harm you. Remember how above I showed above how we could recycle existing methods in our code? Lets say Jane is late on a project, and she needs to add compounding interest to our Account object above. Because she’s in a hurry, she doesn’t have time to study the domain or the previous implementations. She extends the Account object as CompoundingAccount:

public class CompoundingAccount extends Account {

 public void compoundInterest(int periods, double rate){
  int interest = 0;
  for ( i = 0;  i < periods;  i++ ) {
    // more office space arithmetic
    long interest = (long) (interestRate * balance);
    addAmount(interest);
  }
 }
}

This code enters your domain and hundreds of view layers begin using the CompoundingAccount. Next, your company gets sued for not computing interest correctly (as shown here). You, as the domain expert know that in the root object know exactly where to make this change in the Account object. You do so, but because Jane did not obey the DRY principle, she has caused compounding damage to the domain! You could have hundreds of methods and views built on either fireInterest or compoundInterest. Think this won’t happen to you? Be realistic: It’s not a matter of if these events will happen; it’s simply a matter of when they will happen., and if you don’t have a contingency plan or governance, you’ve already failed.

Solution: Recognize DDD is a true double edge sword. As business logic propagates down your domain tree, mistakes will propagate too. Those mistakes will compound as methods are reused, causing a problem. You must setup governance in your domain. You must have centralized domain experts that develop and can tell you the correct place to implement new functionality as your domain expands.

Finally,
Jon’s DDD Antipattern #3: DDD is made ineffective in a distributed environment by most SOA implementations.
This is also frequently omitted. Remember how we said that classes were designed to hold state and business logic together? What happens if we translate the Account object to XML (a very common SOA protocol)

<account>
 <balance>375</balance> 
</account>

Where did my business logic go? It isn’t transferred… Protocols like REST, SOAP, RMI, Hessian transmit the state of business objects, but they do not transmit the implementation. How then, can you transmit an object between two independent systems and guarantee that fireInterestCaculation will create the exact same value? You actually can’t. The implementations on both ends of the wire must be the same, which you can’t guarantee in a distributed environment.

Solution: The only thing I can think of right now that might come close to resolving this issue the little used feature of Java RMI called Remote Class Loading. Remote Class Loading will sense that a client does not have an implementation, and load the implementation across the network. Maybe this is a solution? I haven’t experimented with this.

So as I sit here slicing my pancakes, I’ll admit I’m not opposed to DDD. In my career, DDD has simply been an innocent bystander in the war of egos vs time-schedules. The problems with DDD have been in its execution, not it’s integrity. We’ve all heard the benefits of DDD many times over, but I see very little discussion about how to manage its pitfalls. How do you manage DDD effectively? Have you any stories how DDD has because ineffectual? How do you deal with change management in your domain?

Once again, thank you for reading!

Advertisements
Comments
  1. Shaun says:

    Your assertion that DDD is made ineffective in a distributed environment is completely false. The DDD implementation lives behind the web service interface and the domain is not exposed to the client. Only data crosses the wire. The client makes web service calls to invoke the domain specific behavior and data comes back. That’s the point of SOA. Multiple clients from different domains can make use of that service. Hopefully each client will wrap the data passed back in their own domain specific type or service, which is known in DDD terms as an anti-corruption layer.

  2. Jonathan says:

    Shaun, are you talking external or internal webservices? It may not matter, but “by deeply connecting the implementation to an evolving model of the core business concepts”, having each client have a different implementation seems to fragment your business logic. Are you saying past the anti-corruption barrier, only the balance would be available, not the actual domain object?

  3. Shaun says:

    Internal or external doesn’t matter. You’ve created a web service to provide access to some complex domain, in this case banking. That webservice exposes all the functionality you’re going to let clients carry out. Those clients will be accessing that service from various other domains each with their own nomenclature and behaviour. When the client needs to invoke business logic related to banking they call the webservice.

    Lets say your banking webservice is accessed by an online gambling site and an online retailer. Each of them has their own domain. One deals with spreads and bets the other with products and inventory. Your banking web service provides them the financial services they need to charge their customers. They will create a FinancialServices service class with a charge(Account acct) method. This method simply makes calls to your webservice to ensure funds are available and if so makes another call to perform the money transfer. Their Account object will have an account and routing number and any other properties and behavior relevant to their domain. Your Account object behind the webservice provides account balances and handles withdrawals. As you cross domains the context and behaviour of the Account object changes. There probably will be some similar properties between the two, but when you speak of Account it is something different than when they speak of Account. So the business logic isn’t fragmented because the context and usage is different on each side of the webservice.

  4. Jonathan says:

    Ah, I see. So for example, your proposed webservice would offer the same methods available on the Account object? If this is so, I could see that working, and the ideas of SOA and DDD would be come complementary at that point.

    • Jonathan says:

      Hey guys, keep the comments friendly. Shaun was able to present a dissenting opinion in a respectful manner. I am not attacking you in my post, so dont attack me. You are encouraged to disagree with me, but you must edit your comnents first.

  5. Dimitris says:

    #1 & #2 are human factor faults and can be mitigated with proper unit testing.

    #3 is the real issue with DDD. SOA is the perfect architecture for distributed systems. Services are geared for remoting and Entities for serializing. This is not so because of bad design, but because as a solution fits the problem well.

    PS. Oh and IMO “anemic domain” is not so anemic after all. Getters & Setters do provide data encapsulation, e.g. can make attributes read only, or perform custom initialization or perform low level validation, etc. Also Services most certainly make use of polymorphism even though transparently, e.g. transaction management.

  6. Programmers have to understand the business logic even if they are placing it in the service instead of the domain. Its just that with DDD they need to take a slightly different approach (while armed with the same pieces of knowledge about the domain and the same technologies).

  7. Al says:

    Shaun, your right, SOA is about integrating systems. So each system must be isolated from each other.

    That being said Jon, when you talk about internal web services, I guess your wondering about crossing layers in an application when the presentation talks to the model via REST or SOAP services (ex. iPhone or HTML5 MVC apps). I think in those cases, front-end apps must be seen has different systems. Maybe one day we’ll have dev tools that safely abstract the communication implementation between layers, thus allowing us to transport full rich domain objects. I know they’re working on this with JPA… but we’re not there yet. It would significantly reduce validation code!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s