So far in this chapter, I have mostly talked about the logical structure of the Domain Model, which is just one dimension of the picture, of course. There are others. I'd also like to discuss different execution styles for a while. Let's start with the deployment environment set up for the feature list. There, I said that we are creating a rich client (WinForms) application without an Application Server. It could look similar to Figure 4-16.
Figure 4-16. WinForms and no Application Server
For the sake of the discussion here, let's increase the complexity quite a bit for a moment and add a requirement of an application server as shown in Figure 4-17.
Figure 4-17. WinForms and Application Server
The first question is on what tier(s) should the Domain Model execute? Should it only execute on the Application Server and then you give out dumb data structures, Data Transfer Objects (DTO) [Fowler PoEAA] to the Consumer tier? Or should the Domain Model only execute on the consumer side, and you fill it by asking for DTOs from the Application server? Or perhaps you want the Domain Model to execute on both tiers? Perhaps two different Domain Models?
It's important to think about the purpose of the application server, that it's really needed and that you really need to distribute the Domain Model. Remember Fowler's First Law of Distributed Object Design: Don't distribute your objects! [Fowler PoEAA]
Let's assume for a second that the Domain Model should (at least) execute at the Application server. Should it then be a shared Domain Model instantiation, so that there is a single instance for representing a single customer to be used by all users? Or should it be one Domain Model instantiation per user or per session so that there are several instances representing a certain customer at a certain point in time?
The third question is, should the Domain Model instantiation be stateful between calls, or should it go away after each call?
Fourth, should we try to build up a complete Domain Model instantiation by fetching as much data as possible at each request, and never let go of instances?
We have lots of questions and, as always, the answers depend on the situation. One problem with the discussion is that I haven't (in a DDD-ish manner) decided too much about the infrastructure.
Anyway, I'd like to say a few words about how I usually prefer to deal with this.
What I mean with the term "Domain Model instantiation" is a set of instances of the Domain Model instead of its classes. Of course, we are sharing the classes between users much more often than the instances. Yet I think this term adds clarity to the discussion.
Location of the Domain Model
First, if I'm in control of both the consumer and the Application Server, I think it might be fine to expose and use the Domain Model both at the consumer and the Application Server. If we don't use it at both places, there is a risk of unnecessary work and less power because then we might create two similar, but slightly different, Domain Models, one being for the consumer and one for the Application Server. We also need adapters that can transform between the two Domain Models. (The most important thing here is probably to be very aware of which situation you haveone model or two. If you think it's one but it's actually two in reality, you will run into subtle problems.)
It's important to mention, for complex scenarios I'm fond of Presentation Model [Fowler PoEAA2] as the UI-optimized view or version of the Domain Model.
Isolating or Sharing Instances
At the application server, should we have shared Domain Model instantiaton, per user or per session? Well, I like the idea of a shared Domain Model instantiation in theory, but in practice I prefer to stay away from it most often. It gets much more complex than what you would first expect, so I go for a Domain Model instantiation per user instead or actually usually per session. It's much simpler.
One of the main problems with a shared set of instantiated domain objects occurs if it has to execute in a distributed fashion, perhaps on two application servers in a "shared nothing" cluster. We then have the problem of distributed caching, and that's a tricky problem (to make an understatement), at least if you need real-time consistency. It is so tricky that I have given up on it for now as far as finding a general solution that can scale up well. However, for specific situations, we can find good enough solutions. Of course, if we have all Domain Model instances in memory, there are probably fewer situations where we need to scale out, at least for efficiency reasons. Still, if we do have the problem, it's a tough one to solve in a good way.
The term "shared nothing" cluster needs a short explanation. What I'm aiming at is that the application servers aren't sharing either CPU, disk, or memory. They are totally independent of each other, which is beneficial for scalability.
To read much more about this I recommend Pfister's In Search of Clusters [Pfister Wolfpack].
Stateful or Stateless Domain Model Instantiation
At the application server I don't let the Domain Model instantiation be stateful, and I toss it out between requests. On the consumer side, on the other hand, I try to keep the Domain Model instantiation around between requests, but often not after the use case is done.
Complete or Subset Instantiation of the Domain Model
Finally, I don't try to instantiate the complete Domain Model. At the consumer that would just be plain stupid in cases where the database is moderately large or bigger. Instead, I use the old way of thinking that says fetch what you need and nothing else. And when I'm done with it, I toss it out, making room for other data, and don't hold on to old data for too long. On the other hand, when it comes to static data, it's different. We should try to cache read-only data as much as possible.
There is an open source project called Prevayler [Prevayler] (and there are others that are similar) that supports Domain Model instantiations that are stateful, shared, and complete. So that means more or less that the Domain Model and the database are the same thing. For that to be possible, we need large amounts of RAM, but that's less of a problem today because memory prices are falling, and 64-bit machines are becoming commonplace.
The idea is that it writes to a sequential log file each time there is a change to the database. If the power goes, the Domain Model instantiation can come back by reading an image created at a certain point in time and reading through the log file. This is pretty simple and extremely efficient execution-wise because all data is in memory.
What we have then is basically an object database, but a simple one because there is no need for faulting or other such things. All instances are in memory all the time, and there is no need for O/R Mapping from/ to a relational database, not even from/to an object database. There is just the Domain Model instantiation.
OK, from now on in this book I will leave the added complexity because of the application server and just assume that we work with the rich client including the Domain Model or a web application, which again talks directly to the Domain Model.
One reason for this decision is that I think the classic way of using application servers is in a way disappearing, or at least changing. What's the driving force regarding that? Perhaps SOA, but that's another story (which we talk a bit about again in Chapter 10).