Before moving into architectures for Domain Driven Design based projects it is important to start off by analyzing what is generally considered to be the standard architecture that many try to apply to projects. We can from that point attempt to improve upon the stereotypical architecture in small rational steps while trying to minimize the cost in terms of productivity for each step towards a better architecture.
Below is shown a diagram of a stereotypical architecture.
Figure 1 A Stereotypical Architecture
The above architecture is centered upon a backing data storage system. This system although typically a RDBMS does not have to be, it could just as easily be a key/value store, and object database, or even plain XML files. The important aspect of the backing store is that it is representing the current state of objects in the domain.
Above the backing data storage lies an Application Server. An area of logic, labeled as the domain in Figure 1 contains the business logic of the system. In this area validation and orchestration logic exists for the processing of requests given to the Application Server.
It is important to note that although Figure 1 is drawn without a data tier one could place a data tier in between the Application Server and the Data Storage. It is also important to note that a “domain” is not necessary to achieve this architecture, one could also use other patterns such as Table Module or Transaction Script. With these only existing as Application Services.
Abstracting the “domain” one will find a facade known as Application Services. Application Services provide a simple interface to the domain and underlying data, they also limit coupling between the consumers of the domain and the domain itself.
On the outside of the Application Server sits some type of Remote Facade. This could be many things such as SOAP, custom TCP/IP, XML over HTTP, TomCat, or even a person manually typing messages that arrive tied to the legs of pigeons. The Remote Facade may or may not be abstracted away from its underlying technology mechanism depending on the situation and tools that are involved.
The overall usage of an Application Server to abstract away the data storage of a system and to provide a centralized location of business logic has become very popular over the years and at the time of this writing is in many circumstances considered to be the default architecture applied to many systems.
Interacting with the Application Server there is a / are many client(s). The general interaction of the clients can be seen in Figure 2.
Figure 2 Typical Client Interaction
The basic interaction of the client can be described as a DTO (Data Transfer Object) up/down interaction. Going through the lifecycle of an operation is the easiest way to show the functioning of the API. A user goes to a screen, perhaps to edit a customer. The client sends a request to the remote facade for a DTO representing Customer #id. The Remote facade loads up the domain objects required, and maps the domain objects to a DTO that is then returned to the client. An example of DTO in XML format can be seen in Figure 3 but the basic explanation is that the DTO in this stereotypical architecture contains the current state of the object in questions.
The client will then display the information received from the Remote Facade on the screen allowing the user to interact with it. This is very often done utilizing a view model and/or data binding with the view.
At some point the user will be complete with the editing of the data on the screen and will through some action cause the UI to “Save” the data. Generally this is implemented through a “Save Button” although some User Interfaces will instead just have you leave the current data which forces a save.
Figure 3 Example in XML of a DTO
The processing of a Save on the client will take the data that has been edited by the user on the screen, pack it back into a DTO (usually identical to the DTO it requested from the Remote Façade for displaying to the user ). It will then send this DTO back up to the Application Server.
The Application Server receiving the DTO will then start a transaction/session, map the DTO back to the domain objects, allow the domain objects to verify any changes, then save the changes within the domain objects back to the data storage likely through the use of something like an Object Relational Mapper that has the ability to distinguish what has changed in the domain objects and update the data storage accordingly. The Application Server will return to the client either an Acknowledgement (Ack) that the change has been persisted or it will return a series of errors as to why it was unable to persist the changes.
Analysis of the Stereotypical Architecture
The architecture provided above as with any architecture has many properties. Some of these properties are good under certain scenarios and other properties can be extremely bad in others. As architects we should really be trying to align many of these properties to best fit our needs.
When looking at properties it is important to note what the most likely property is for a given architecture becoming popular. In the architecture above the most likely property defining its popularity is that it is simple. One could teach a Junior developer how to interact with a system built using this architecture in a very short period of time. Going along with the simplicity, the architecture is completely generic. One could use this architecture on every project. Along with being able to use it on every project, because many people are doing it, its likely that if a team brings on a new member the new member will be intimately familiar with the general architecture of their system again lowering on ramp up costs.
The combination of these two items allows teams to become extremely adept at applying this architecture and more important it allows them to use it as a default architecture. The thought process of needing to align non-functional requirements really goes away as they know that this architecture will be “good enough” for 80% of the projects that they run into.
Many frameworks exist for the optimization of time required to create systems utilizing the architecture provided above. ORM’s are the largest single example as they provide valuable services such as change tracking and transaction management with complex object graphs. Other examples would include the automapping frameworks that map from the domain objects to DTOs on both sides resulting in largely removing the amount of “plumbing code” required to map the DTOs back and forth in the Application Server.
Of course there are however also many not-so-good things associated with the architecture provided above. It being that this document is associated with Domain Driven Design the single most important of the not-so-good properties would be that it is impossible to apply Domain Driven Design with the architecture provided.
Domain Driven Design
The application of Domain Driven Design is not possible in the above architecture though many who are “practicing” Domain Driven Design use this architecture. The reasoning for why it is impossible can easily be seen when one looks at how the Ubiquitous Language is represented by the object model.
In the architecture above there are only four verbs (and of course synonyms for those four such as edit instead of update). The four verbs are Create, Read, Update, and Delete or CRUD as they are commonly known in the industry. Because the Remote Façade has a data oriented interface the Application Services must necessarily have the same interface.
This means that there are no other verbs within the domain. When however one talks with domain experts in an effort to refine an Ubiquitous Language, it is extremely rare that one ends up with a language that is focused on these four verbs.
There is a related well-known anti-pattern of domain modeling known as an “Anemic Model”.
“The basic symptom of an Anemic Domain Model is that at first blush it looks like the real thing. There are objects, many named after the nouns in the domain space, and these objects are connected with the rich relationships and structure that true domain models have. The catch comes when you look at the behavior, and you realize that there is hardly any behavior on these objects, making them little more than bags of getters and setters. Indeed these models often come with design rules that say that you are not to put any domain logic into the domain objects. Instead there are a set of service objects which capture all of the domain logic. These services live on top of the domain model and use the domain model for data”
The model that is being built in this architecture sounds at first to be an anemic domain model. Because the Application Services map data back and forth to DTO’s the domain objects have little behavior and are littered with getters and setters to be used in the mapping process. There is a structure to the domain showing how objects relate with one another but …
One cannot even create and Anemic Domain Model with this architecture as then all of the business logic would be in services, here the services themselves are really just mapping DTO’s to domain objects, there is no actual business logic in them. In this case a large amount of business logic is not existing in the domain at all, nor in the Application Server, it may exist on the client but more likely it exists on either pieces of paper in a manual or in the heads of the people using the system.
Architectures like the one being viewed tend to come with instructions of how to complete complex tasks by editing data in many parts of the system. A stereotypical example of this would be when changing the sex of an employee you must after go edit their health insurance information. This is far worse than the creation of an anemic model, this is the creation of a glorified excel spreadsheet.
When one looks at the architecture provided above in the context of scaling one will quickly notice that there is a large bottle neck. The bottleneck in terms of scaling is the data storage. When using a RDBMS as 90%+ currently use this becomes even more of a problem most RDBMS are at this point not horizontally scalable and vertically scaling becomes prohibitively expensive very quickly. It is however also extremely important to remember that most systems do not need to scale and as such scalability is really not a grave issue in all cases.
The DTO up/down architecture employed on many projects is capable of being used for many applications and can offer many benefits in terms of simplicity for teams to work with. It cannot however be used with a Domain Driven Design based project, to attempt so will bring failure to your efforts at applying Domain Driven Design.
This architecture does however make a good baseline and the rest of this document will be focused on improving this architecture in incremental steps while attempting to limit or remove cost while adding business value at each additional step.
- Fowler, M. (2003, 11 25). MF Bliki: AnemicDomainModel. Retrieved from Bliki: http://martinfowler.com/bliki/anemicdomainmodel
The following seems like a non-sequitur to me:
“In the architecture above there are only four verbs… The four verbs are Create, Read, Update, and Delete… Because the Remote Façade has a data oriented interface the Application Services must necessarily have the same interface.”
I see nothing in the diagram or previous description that requires the Remote Facade only include data-oriented interfaces. Why, for example, can’t the Remote Facade and the Application Service have a method like TransferMoney(int debitAccount, int creditAccount, decimal amount), which results in the Application Service using that input to retrieve and coordinate domain objects and domain services to perform the action requested by the user? That doesn’t seem CRUD like and seems very much in line with what I understand of DDD. Can you clarify?
OK, after reading the task-based UI article I see that my example is an example of a Command, and I see that in the Stereotypical Architecture the assumption is that the same DTO is being sent down and up, hence the idea that the only verbs are CRUD.
The comment about instructions outside the system being a smell of CRUD rings sooo true!