My friend Mark Needham wrote a blog post on the Domain Model pattern and Domain-Driven Design recently. He changed a bit the contents but the original question was: Should we always use Domain-Driven Design? In response, the author gave an overview of several architectural patterns for domain logic.
I pointed out in a comment that I think he was making the wrong comparison.
I think you may have made a confusion, Mark.
You’ve presented as alternatives techniques that are at the same level as the Domain Model pattern, not Domain-Driven Design.
It is very hard to have DDD without the Domain Model pattern but you don’t have to use DDD just because you have a Domain Model. DDD is about using business language in the software constructs, Domain Model is just about having proper objects (and not just functions and data structures) as the business logic holders.
So, in the end of the day the answer to your question would be ‘no’, but not just because you could use transaction scripts or table gateways but also because you could have a Domain Model that’s not designed after DDD principles.
So he invited me to share my thoughts on the subject and that’s what I’ll be doing in the following paragraphs.
It’s All About Complexity (again)
Ultimately, software design is about managing complexity.
You can write a very complex program without any modularisation and still have a fantastic system, at least for its users, but that piece of code will be virtually impossible to change -if you don’t know what I’m talking about probably you’ve never wrote or read a game in BASIC.
In order to keep complexity manageable we generally break our code into modules. The problem is that there are dozens of options of how to shape those modules, and each one of those spawns lots of more options about its internals.
For this text, I’ll divide those options into three levels:
- Level 1: What to use as our main modularity unit?
- Level 2: How do modules collaborate?
- Level 3: What modules look like?
My comment on Mark’s post was motivated by the fact that I think he was comparing something in the third level with something in the second one.
Level 1: What to use as our main modularity unit?
This is a choice of which paradigm or paradigms you choose for your system. Among the possibilities, you could use procedures, functions (as in Functional Programming, not as in procedures-that-return-something) or objects.
In a normal Domain-Driven Design use case we would choose objects at this level. It’s not exactly necessary to use objects as people use the same concepts that we call “Domain-Driven Design” in other paradigms, but that’s the common case.
Level 2: How do modules collaborate?
Ok, we are using objects, but that doesn’t say much. People often think that Object-Orientation means “model the real world”. Well, not exactly.
Objects are just a way to organize code in a modular way. In “The Early Story of Smalltalk”, Alan Kay talks about how the concept was conceived:
Though OOP came from many motivations, two were central. The large scale one was to find a better module scheme for complex systems involving hiding of details, and the small scale one was to find a more flexible version of assignment, and then to try to eliminate it altogether. As with most new ideas, it originally happened in isolated fits and starts.
So, I in the first level we decided that we were going to use objects; we now must decide how do we use those. And that’s a time in when Martin Fowler’s patterns come handy. In that book, Martin has catalogued three patterns for domain logic organization:
- Transaction Script: “A Transaction Script organizes all this logic primarily as a single procedure, making calls directly to the database or through a thin database wrapper. Each transaction will have its own Transaction Script, although common subtasks can be broken into subprocedures.”
- Table Module: “A Table Module organizes domain logic with one class per table in the data- base, and a single instance of a class contains the various procedures that will act on the data.”
- Domain Model: “A Domain Model creates a web of interconnected objects, where each object represents some meaningful individual, whether as large as a corporation or as small as a single line on an order form.”
People usually assume that the first two options are not really Object-Oriented. I tend to disagree with that. For me, a Transaction Script is an object that models a procedure, but it’s still an object. The Table Gateway is an object that encapsulates a table, but it’s still an object.
And then comes the Domain Model, the usual choice to implement Domain-Driven Design. By deciding to use a Domain Model we intend to model our problem as a group of collaborative objects that model entities instead of processes or tables.
Once again, Domain Model is not about “the real world”. As Eric Evans says:
Domain modelling is not a matter of making as “realistic” a model as possible. Even in a domain of tangible real-world things, our model is an artificial creation. Nor is it just the construction of a software mechanism that gives the necessary results. It is more like moviemaking, loosely representing reality to a particular purpose. Even a documentary film does not show unedited real life. Just as a moviemaker selects aspects of experience and presents them in an idiosyncratic way to tell a story or make a point, a domain modeller chooses a particular model for its utility.
Level 3: What modules look like?
We have objects and we decided to model our domain as small collaborative pieces. Now what should be part of our model?
I don’t know much structured approaches for this level but I can think of two big groups: Manual Model Transformation and Model-Driven Design.
Manual Model Transformation is how I like to call the older and still most used option. It became popular in the Object-Oriented community with the RUP and consists of having more than one model. You still have a Domain Model but this is more of a high-level, non-executable and non-verifiable artefact. The actual model that is written in code derives from that abstract Domain Model but may be very different from it.
As an example, let’s examine how classic RUP deals with that. According to RUP, the purpose of analysis is “to transform the requirements of the system into a form that maps well to the software designer’s area of concern”. The purpose of design is “to adapt the results of analysis to the constraints imposed by non-functional requirements”.
In RUP we have a Analysis Model that originates a Design Model. The process even talks about “design classes” and “analysis classes” as separate things. Here’s an explanation about those models from the RUP book:
Generally, there is one design model of the system; analysis produces a rough sketch of the system, which is refined further in design. The upper layers of this model describe the application-specific, or more analysis-oriented, aspects of the system. Using a single model reduces the number of artefacts that must be maintained in a consistent state.
I think this approach has lots of problems. The biggest one is the fact that, as the name suggests, you have to manually (mentally) convert from one model to the other all the time. Whenever a change is needed you have to alter the “Analysis Model” and then change the “Design Model” accordingly.
That said, this could be useful in some scenarios like trying to understand legacy systems or dealing with non-friendly or lower-level technology.
You may think that after “manual mapping” I will suggest “automated mapping” as an alternative. Not really as I don’t think this field is mature enough, but this is the subject of a future text. At this time the other option I will propose is to remove the mapping altogether, using Model-Driven Design.
Going Model-Driven we assume that there’s no value in having two separate and dependent models, as whatever is in one of them should be mapped to the other and mapping is hard work. Therefore we create only one model and use it in all disciplines.
It’s hard to get a good definition for Model-Driven Development so I will use Eric Evans’:
MODEL-DRIVEN DESIGN discards the dichotomy of analysis model and design to search out a single model that serves both purposes. Setting aside purely technical issues, each object in the design plays a conceptual role described in the model. This requires us to be more demanding of the chosen model, since it must fulfil two quite different objectives.
Having only one model will avoid most issues raised above but we still have one big issue: the person modelling the domain is not the domain expert.
We would give developers the responsibility to create the Domain Model. The developers will talk to the business experts, take some notes and create what they think is a good Domain Model. Who can verify that the model is correct?
To the domain expert that Domain Model -constructed in a Model-Driven way or not- is a black box. She can’t understand all those programming languages concepts, she has no idea what SecurityDecorator means on that diagram (she can’t recall any decorator in her business, she works at a bank!).
So, finally, here comes the third approach, extending Model-Driven Development with a business-centric view. Eric Evans has named it Domain-Driven Design.
Domain-Driven Design is more of a philosophy than a technique. Whole books were written about it but I think it is fair to summarise it as defining a common language between business and software people and using this language in the model.
Using Domain-Driven Design the model expressed in software is the model that the business understands. In Domain-Driven Design the model is not a block box to the domain expert, actually I’d say that the domain expert is the main modeller of the system.
But can we have the model expressed in a common language (Ubiquitous Language is the term used in Domain-Driven Design) and not have Model-Driven Design? Yes, you can, but I won’t call it Domain-Driven Design. Actually, most “Analysis Models” I’ve seem use some kind of common language, the problem is that the “Design Model” tend to not use that. The difference in Evans’ work is the use of the language everywhere.
Note: I’m not claiming that Evans invented the concepts behind Domain-Driven Design. His work is about getting the concepts structured in a way that can be easily implemented in most modern platforms.
Options are a Good Thing(tm)
We saw a bunch of options in three different levels. This is a gross simplification of what one faces when developing software –in the real world we have much more options and much more levels.
Having options is a good thing and a good designer must always look for different approaches. Although Domain-Driven Design is the current hype sometimes it is just not practical for some reasons, and not only technical reasons.