This is not a fully fledged introduction to Domain Driven Design (DDD). If you want that, go get Eric Evans’ book, Domain Driven Design. It is definately worth it and I whole heartedly recommend reading it. Instead, what I’ve got here is a general overview of what a “Domain Model” is, how it differs from a typical “MVC Model” and why a Domain Layer is a good thing to have in any large scale application. This applies to any language really, but I’m primarily a PHP guy, so my points of reference are all Zend Framework based. It’s a fairly large topic, I’m aware that I’m barely scratching the surface, and as usual I welcome your comments!
What are Models?
A very good question! One definition of “Models”, and probably the one you’re thinking of, is that they are one third of the Model-View-Controller (MVC) architecture. In my opinion, the most important yet also the most undervalued and misunderstood. I’m not going to go into the merits of MVC here, but the basic aim of the MVC architecture is to separate application code into three distinct areas, Data, Presentation and Logic. In this context, “Models” are Data.
Let’s remind ourselves of some other definitions of “model”. To a mathematician, a model might be an equation. To an artist, it’s probably something sculpted, or the original subject matter itself. To an architect, it might be a small-scale representation of a building. To a particle physicist, it’s probably some kind of simulation. Just remember that the definition of “model” is somewhat varied. Not to mention, that was before software developers came along and invented about a hundred and one different definitions of our own! So, if you’re confused by it all, it’s totally understandable! The thing to keep in mind, is that what MVC calls a Model and what Domain Driven Design calls a model, or Entity as Eric Evans calls them in his book, are subtly different things.
Models in MVC
Whenever I start a new PHP project, I first lay down Zend Framework. That’s just my preference. Zend provides Views through Zend_View, Controllers through Zend_Controller and Models through, Zend_Model? No, there isn’t one of those. Ok, Zend_Db then? Well, yes and no. The truth is, Zend Framework is only really a VC framework, there’s no M as such.
As far as MVC is concerned, “models” are just your interface to data. That’s all really. So from that viewpoint, it’s perfectly acceptable to use an Active Record style interface like the one that Zend_Db provides. Those objects act as an interface to your database schema. If your application’s requirements are simple enough, then you can get away with just that. It qualifies as MVC and gets the job done. But don’t be fooled, while it is a “Model”, it’s not a “Domain Model”. Besides, since when was anything ever that simple?
What is a Domain Model?
One of the problems is that the terms “Model” (from MVC) and “Domain Model” (from DDD) have had their definitions stretched so much that it’s becoming unclear whether or not they’re the same thing, let alone what either one is. To a lot of people, the terms “domain model” and “model” have become interchangeable. Unfortunately, that’s wrong. There is more to Domain Models than just moving data from the database, to your application and back again. Things like Zend_Db are actually Data Access Objects. Since that’s what they do.
In some ways the Domain Model is a diagram, in others it’s a concept and in the way that most attention is given to, it’s a piece of code. But funnily enough, a Domain Model has very little to do with code at all, code is just the final representation of it.
When you think about a particular piece of software, say a system that controls a railway network, you build up a picture of everything in the system. So, you have trains comprised of one engine and one or more carriage. Those trains stop at stations, located along lines of railway tracks, connected with junctions, controlled by signals.
That is the beginning of a Domain Model. It is a conceptual model that contains all the tracked objects or Entities in the system and how they’re related to each other. Also known as “a clear picture of what’s going on” or business logic. These Entities are what many call their “Domain Models”.
Trains and stations are significant, and thus entities, but an object representing a time-stamp of when a train is going to arrive at a station or the money object representing the ticket cost isn’t. Note I said “ticket cost” and not “fare”. So I’m referring simply to the object holding the value, or the Value Object. An instance of Zend_Currency is a good example of a value object.
Thin Controller/Fat Model
Approaching from an MVC viewpoint, you may have come across the phrase “Thin Controller/Fat Model”. This is half-way towards a Domain Layer. In our trains example, you may have a controller action for finding the train you want to catch between two stations. For the sake of argument, we’ll say it’s part of the public facing website and is triggered with an HTTP request.
What are some possible steps you’d have to take to determine what train someone should get? Well, where are you coming from? Where are you going to? How many people want to travel? When do you want to leave or arrive? Will you be taking a bicycle with you? What class ticket do you want? Do you want a reserved seat? How many connections are you willing to make? There’s quite a bit that needs to go into deciding what train services to recommend to someone.
In a typical MVC scenario, the controller would take all that input from the HTTP request object, it would make requests to the database, search the time tables in accordance with your business rules and come up with a list of possible trains that could be caught. That then gets passed to the view for presentation to the user.
When you adopt a Thin Controller/Fat Model methodology, you would simply move all that business logic into your MVC Models. So for example you might call a method on your Train Model called “findTrains” that accepts a list of criteria to satisfy when searching for train services. At this point all your controller is doing is marshalling the data from the HTTP request parameters and passing it straight into the Model. Your “findTrains” method crunches away and eventually returns a result set. The controller then simply takes that and hands it to the view object for presentation to the user.
Thus, your controller doesn’t contain a great deal of code, or at least, doesn’t do any real crunching. Instead, the model does it all, rendering the controller “thin” and the model “fat”. I say this is a half step towards a Domain Layer because it at least it contains all the business logic to one part of your system, the models.
Now the only thing your controllers need to worry about are the stages up to invoking your business logic, and deciding how to output the result. Things like massaging input parameters in preparation to being fed into the models, or emailing the results rather than displaying them on a page. The same controller action that finds train services for the website, could now also provide a Json-RPC interface without interfering with the business logic in any way, those files remain untouched. The more your changes are contained, the less likely it is that there will be any knock-on consequences, also known as, bugs!
Ultimately, if you want your software to become a long-term success, you’ve got to accept that it’s going to become bigger than any one developer, team or company. It’ll take on a life force of it’s own. It will grow and evolve with the passage of time and the ebbs and flows of demands being made on it. It needs to be highly adaptable, but also reliable. Perhaps most importantly of all, especially if you want this software to last, is that it must be easy to maintain. Specifically, by developers who aren’t you, or aren’t members of the original team.
By having a clear and concise Domain Model, we can we guide it’s evolution and ensure a result that we want. It’s software husbandry, just like with animals. If you don’t keep on top of it, then you’ll end up inbreeding your codebase and you’ll have all kinds of unforeseen consequences on your hands before you know it. But also, just like animals, your code has to be friendly. Both user friendly and developer friendly.
Ok, but what does that mean in technical terms? What does having a domain layer in my application buy me exactly? Well, what it essentially allows you to do is create your application as, for want of a better phrase, “pure code”. That is to say it has no persistence, no interfaces and generally has no concept of anything outside it’s own domain. It has no idea what an HTTP request is, let alone how to map a URL to a dynamically generated page. It doesn’t matter one iota to the domain whether the request was made through HTTP, HTTPS, XMPP, or MQTT. The method by which the request came in is irrelevant when determining the arrival time of the next train to London Waterloo!
Your controllers will become a lot more concise, since you can then interact with your business logic like it was any other library. Rather than opening and parsing files, querying databases and performing calculations right in front of you, all that gets packaged away into methods and neatly labelled. You end up breaking down all your business logic into very small methods, which has the additional benefit that it makes unit testing your business logic a darn sight easier because you know where it all is, and you can test it directly!
By implementing a Domain layer in your system it makes it a relatively trivial matter to add or remove interfaces. Suddenly there’s demand for a REST interface? No problem. Nobody uses the IRC interface anymore, pull it out. It doesn’t make any difference to the domain or any other interfaces to it that you’ve set up. The same goes for data persistence. If you have to port your application from MySQL to CouchDB, you can do that without any risk to your business logic.