I recently did an interview for one of my favorite podcasts, .NET Rocks. In the episode, I talked with Richard Campbell and Carl Franklin about Domain-Driven Design, why I think it is so appealing, what I see as a misplaced emphasis in most of the materials I’ve heard regarding it, and what I think the emphasis should be instead. I really like the way the interview went and the way it turned out. You should listen to it.
In that interview, I laid bare my position on the ideas that are important to Domain-Driven Design. The points of emphasis I see (and from what I can gather, they are consistent with the views of Eric Evans on this), are these:
- The Ubiquitous Language
- Bounded Contexts
- Core Domain
- Domain Events
- Aggregates, Entities, Value Objects, and the stuff closer to the implementation Evans recommends
The first 4 things on this list are, to me, what make up Domain-Driven Design. Using these concepts, you can compose a system from autonomous pieces and make them play nice together without having dependencies requiring coordinated development, deployment, and testing (though end-to-end testing is something that should and must happen, it’s not a dependency for independent operation of teams). This is where DDD has enormous value. The fifth item is advice on how to build your system inside a bounded context. This information is nice and in some bounded contexts it prescribes practices and patterns useful in managing the modeling of the problem. It’s not always the best way to build the guts of a particular bounded context. It’s a tool in the toolbox that should be used where and when appropriate. It’s not the core of what makes DDD appealing.
One of the things I brought up in the interview on .NET Rocks was something I saw in a presentation recently at the Denver .NET Meetup. Paul Rayner was giving a presentation about using SpecFlow with Visual Studio to practice Behavior-Driven Development. He was asserting that features should be expressed in the terms of the business (the Ubiquitous Language, to use the vocabulary of Domain-Driven Design) and to avoid technical terms like requests and responses and HTTP status codes and user interface terms like . There was some disagreement in the audience as to whether rigor on this was really necessary, or if just talking in terms of the user interface is good enough. It seems to me it’s pretty broadly agreed and obvious that in BDD, technical terms are bad, but the prohibition on user interface elements is received much less warmly.
The intent of Behavior-Driven Development is, first and foremost, communication. The collaboration of technical experts and domain experts is facilitated by the practice. This is why Behavior-Driven Development is useful. I have heard many people say that they are practicing BDD simply because they are using Cucumber or one of its ports on another tech stack. This, though, is not the case. If you are using Cucumber or a derivative to automate tests without constructing features as a team, you are writing tests, not describing behaviors or creating specification by example. It might be TDD (that’s not even guaranteed given only that you are testing), but it’s not BDD. The collaboration that makes BDD useful is what makes it BDD. Not coincidentally, this is also why Domain-Driven Design is useful. As an alternative to technical persons meeting in a room with business persons and then going away with their understandings and interpretations to design, test, and implement software, DDD and BDD emphasize whole teams working together to specify what the software should do. In his post where he coined the term Behavior-Driven Development, Dan North (ok, I’ll go with Behaviour-Driven as a tip of the hat to Dan North) references Eric Evans’s book and explicitly uses the DDD term “ubiquitous language”. This is the thing that makes BDD an evolutionary step beyond Test-Driven Development. Instead of writing tests to help the design of the system emerge, practitioners of Behaviour-Driven Development collaborate with domain experts to specify the intent of the system.
In describing intent, we are talking about activities. We are talking about actions users will take and the expected outcomes. We’re not talking about users, though. We’re talking about accountants, or oil rig workers, or dolphins (the users of your system have personas, they aren’t just users – if your users are dolphins, please let me know, that sounds interesting). A user wants to do something. That something does not involve putting a row in a database table or requesting a page or clicking a button. A student wants to consume a lecture. A lawyer wants to file a brief (I don’t know what that means, but if I was designing a system for lawyers to use to file briefs, it would be my job to know).
There is not a binary criterion for distinguishing good and bad features. It is a continuum with many shades of gray (more than 50) in between. Describing the behavior of a user interacting with a system with user interface terms is certainly an improvement over using technical terms. Describing the behavior of a user interacting with a system with domain terms is certainly an improvement over using user interface terms.
I am reminded of a post I read years ago about using Selenium to drive a browser in integration tests using SpecFlow. Because the intent of the post was to show test automation with SpecFlow and Selenium working together, we can forgive that the language of the feature is given in terms of screens and pages. It illustrates, though, an attempt at describing the feature in the language of the domain that falls just a bit short. Having a feature that looks like this:
Feature: Cart Total
As a shopper
I want to see my cart total on every screen
So I don't have to leave my current page to verify it's contents
@UI
Scenario: Empty Cart
Given I have the Home Page open
Then the cart is empty
@UI
Scenario: Add an Item
Given I have the Home Page open
And I select a genre from the left
And I select an album from the genre page
When I add the album to my cart
Then the cart has a total of 1
is so close to what we really want. It uses domain language and describes what the user wants to accomplish. It still involves too much web page lingo, though. Try this alternative:
Feature: Cart Total
As a shopper
I want to see my cart total
So I can make informed decisions about what I want to buy
@UI
Scenario: Empty Cart
Given I have a new cart
Then the cart is empty
@UI
Scenario: Add an Item
Given I have a new cart
When I add the album “Bad Hair Day” to my cart
Then the cart has a total of 1
Do you see the difference? Removing information about page context and what I see in a user interface and focusing instead on the vocabulary of the domain leads to a more readable specification. Yes, spelling out step-by-step the pages and clicks a user must navigate to perform a task maps nicely to lines of code in step definitions. Is this really what you want, though? Do you want a specification that reads like a test, or an example depicting a real-world use of the real-world process (in this case, buying music) you are modeling with software. I’ll take the latter. It serves as a better specification and as better documentation and when a domain expert looks at that document, they see something they could have written (and ideally, they did participate in writing it). This is where the exercise of building software becomes an exercise in determining what users really want and an exercise in delivering value to delighted customers. Focusing on the problem domain instead of the technological medium yields a much clearer picture of what it is that is sought that is more accessible to both business and technical personnel.