While fixing a few bugs in some legacy code that had no tests didn't follow DDD principles, I had an epiphany. Well, maybe I shouldn't say it that way. I had a few concepts reinforced in my mind regarding the importance of state-based testing, specifically using Behavior-Driven Design techniques.
The problems I had were related to business concepts, such as renewing a customer's subscription. Other than the bugs that I fixed, the main problem with the code was that it didn't follow DDD principles. There were no aggregates or entities. Everything was a service, yet the complexity merited a domain model. Yuck.
With a fully encapsulated domain model, you simply have an aggregate. You feed the aggregate events and it publishes events. It's as simple as that. No properties and other methods to call other than Consume where an event is fed into the aggregate.
By having a fully encapsulated model, we find that CQS, BDD, and DDD can intersect in a powerful way. Specifically, if all I can feed the aggregate are messages and all it publishes are messages, everything becomes a state-based test, which then facilitates a BDD style. For example:
Given: A series of events that put the aggregate into a known state.
When: Event XYZ occurs:
Then: Events ABC and 123 are published.
In a real world scenario:
Given: Your account is overdrawn.
When: WithdrawFunds
Then: NonSufficientFunds
Greg mentioned this exact scenario in a message on Yahoo Groups a few months back. After reading that post, I created this blog entry as an empty draft. Encountering those bugs today, which the above scenario would have solved, gave me the opportunity to finish this post.