Lets start with the following question: Where is a car on the left or on the right?
You would probably say that Car is on the Right, and you are correct. But hold on. What is the difference between them? Difference is that Car is not just a huge collection of parts. Car is not even a collection of parts which need to fit together, but a car is much more than that. A good Car starts with vision and carefully written specifications, and it continues with design and with testing. The design is modified based on the testing results. Only after that each part could be assembled together.
Software development is similar, we cannot just sit down and type code.
What is Domain-Driven Design?
We all want to develop our applications, but business requirements nowadays are huge, and we dive in that complexity. And we can get stuck will all of that, because the whole complexity is in the Domain. For example banking system. Do you as a software developer want to know how does it work? Probably no, but you still need to develop your system. The guy from bank stands near you and and he wants you to develop application for him. What is your starting point? Is it table in database or is it Model
(DDD) is is an approach to the design of software, based on the two premises:
* that complex domain designs should be based on a model, and
* that for most software projects, the primary focus should be on the domain and domain logic (as opposed to the particular technology used to implement the system).
In other words the heart of the DDD is Model
and you start developing your application with drawing your model. Model and design you create should shape each other. Model should represent knowledge to the business and it is language your team speak.
To be able effectively design you should build your Model and implementation together in the same time, cultivating a language based on the Model. You should represent knowledge in your Model and continuously distill it. You could do refactoring to it doing brainstorming and experimenting.
So, imagine that guy from bank, say Domain Expert
, stands near you and you start talking. How does that conversion look like? It could be fractured and this creates serious problems to your project. Domain Experts
have their own jargon and the development team has its own. After that daily discussions are disconnected from the terminology embedded into code. So effect of need to translate what you are talking about into code appears, plus misunderstandings possibilities. This all could lead to not good results.
Need for some common language shows up. Eric Evans calls this language as Ubiquitous Language
. Your language is your model, this means that both Domain Experts
should talk the same language which is build on the model. And changes to the Ubiquitous Language
means changes to the Model
. If Experts start using new terms this should be represented in your model, in your code, diagrams and speech.
How could it look?
* Speech (This means daily conversations between team members.)
* Diagrams (Often these are quick diagrams on the whiteboard, which you created with your Experts.)
* Writing (This could be NOT long documents, describing the Model.)
* UML (But there is no place for cumbersome one.)
Let us move to the way you will build your application.
I hope that everybody heard about the Layered Architecture. For the DDD it is very important since we explicitly separate Domain Level
, which lives between our Infrastructure and Application layers, like on picture. Note, that isolating of your Domain Level is very important
User Interface (Presentation Layer)
Responsible for presenting information to the user and interpreting user commands.
This is a thin layer which coordinates the application activity. It does not contain business logic. It does not hold the state of the business objects, but it can hold the state of an application task progress.
This layer contains information about the domain. This is the heart of the business software. The state of business objects is held here. Persistence of the business objects and possibly their state is delegated to the infrastructure layer.
This layer acts as a supporting library for all the other layers. It provides communication between layers, implements persistence for business objects, contains supporting libraries for the user interface layer, etc.
For example, a typical, the interaction of the application, domain and infrastructure could look like this. The user wants to book a flights route, and asks an application service in the application layer to do so. The application tier fetches the relevant domain objects from the infrastructure and invokes relevant methods on them, e g to check security margins to other already booked flights. Once the domain objects have made all checks and updated their status to “decided”, the application service persists the objects to the infrastructure.
The Smart UI "Anti-Pattern"
All this sounds good but simplicity of just calling infrastructure lead into temptation, and after that you see that code generated on button press actually executes hard-coded SQL, less terrible it could be when that is masked under some "helper" classes. But that all is manifestation of the the Smart UI "Anti-Pattern". Could be surprising to you, but it has its advantages/disadvantages. Why not. It will not be a "Anti-Pattern" if it has no advantages.
- Productivity is high for small applications.
- Less capable developers could work with application easily.
- If each screen has its logic it is possible to quickly change product to fit new requirements.
- Maintenance guys can quickly find root of any bug - just put breakpoint on button press.
Looks like a lot of advantages, but let see what are disadvantages. Hope this will convince you to do not use this "Anti-Pattern".
- Integration of applications is difficult except through the database.
- There is no reuse of behavior and no abstractions on business problems.
- Lack of abstractions creates limits to possibilities of refactoring or doing work iterationaly.
- Complexity buries you quickly. If your application grew to the enterprise level and you still have this Smart UI, then you are in a$$.
- And finally you will not be able to migrate to any other design except of replacing each applications.
So, don't lose your mind, if you build your application with layers, do it correctly!
Building blocks of Domain-Driven Design
Eric Evans introduces terminology to the Domain-Driven Design. Accordingly to him there are such building blocks of the DDD: Associations, Entities, Value Objects, Services, Modules (which are parts of the Model) and Aggregates, Repositories, Factories (which deal with Life Cycle of a Domain Object).We will talk about each of the elements.
If your Car has four tires this is association of the model of your Car. This is relation between business elements you work with.
If we were to implement the concept of a Person using a software program, we would probably create a Person class with a series of attributes: name, date of birth, place of birth, etc. For example if you create Patient entity in code it will be represented like Patient class. It is required that for medical system to identify if the patient is the same. Is the patient "Andriy Buday" physician deals with, the same as "Andriy Buday" nurse has a scheduled procedure? So basically Entity
is object that has Attributes
Value Object are similar to the Entities
expect that they do not need Identity
, which is required for Entity
. For example "Ukraine, Lviv" which is Address is Value object since we do not need identity for it. We could have 300 patients in Lviv (Entities) who could share the same address. This (sharing) could even be done thought the code using Flyweight
design pattern to reduce memory needs. But Address is not always Value Object
. This depends on "Who is asking
?". For the post system Address could be an Entity
Consider that your Model has nouns and verbs (they are used in Ubiquitous Language
). You could imagine that nouns are Entities
and Value Objects
. Your verbs are Services
. If you have action or operation related to the Domain concept, but is not natural part of any of your Entity
or Value Object
you could move it into separate item, which is called Service
. Interface of your Service
should operate with terms of of the domain model. And the operations it does are stateless.
For a large and complex application, the model tends to grow bigger and bigger. The model reaches a point where it is hard to talk about as a whole, and understanding the relationships and interactions between different parts becomes difficult. For that reason, it is necessary to organize the model into modules. Modules are used as a method of organizing related concepts and tasks in order to reduce complexity. In code this could be packages or namespaces or even simple folders. The advantage of this is that when you place some classes together in a Module
, you are telling the next developer who looks at your design to think about them together.
is a group of associated objects which are considered as one unit with regard to data changes. The Aggregate
is demarcated by a boundary which separates the objects inside from those outside. Each Aggregate
has one Root
. The Root
is an Entity
, and it is the only object accessible from outside.
The picture shows that your Customer code could access Tire only though the Car instance and there is no other way to do that, since Car is Aggregate to Tire. But could be that part of Car need identity. This could be Engine of your Car, since it has its number and you need to form you taxes. In this case Engine is Entity which lives outside of your Aggregate
We need to deal with Aggregates
since they has direct relation to the Life Cycle of Domain Object
Creation of an object can be a major operation in itself, but complex assembly operations do not fit the responsibility of the created objects. This operations could be accomplished with Factories
. But not always it should be separate object. In scope of DDD Factory
could even be a simple constructor. Two major requirements to the Factory
are: Atomicity - this means that operation of creating an Entity
should also create whole Aggregate
associated with it. Abstracted to the type desired - this means that Factories
should return you types rater than concrete classes. (i.e. Interfaces).
is an object which provide you CRUD operations to your Aggregates
have many advantages, including the following:
- They present clients with a simple model for obtaining persistent objects and managing their life cycle.
- They decouple application and domain design from persistence technology, multiple database strategies, or even multiple data sources.
- They communicate design decisions about object access.
- They allow easy substitution of a dummy implementation, for use in testing (typically using an in-memory collection).
To be more clear how the Client code, Repository and Factory are interacting between each other. Lets take a look on the next sequence diagram:
I hope that diagram is self-descriptive.
Refactoring Toward Deeper Insight
Since the business and its rules are very complicated you are not able to see whole picture at once. If there are some incomes to your Ubiquitous Language
you should represent this into your Model
. You do this with refactoring of existing code in way that brings key concepts into light.
There are three ways to bring concepts into light
- Constraint. For example you could put Constraint into separate method to make it more explicit.
- Process. This stands for introducing your Services. If process is difficult we can encapsulate algorithm into object and use a Strategy.
- Specification. A Specification is used to test and object to see if it satisfies a certain criteria. Specification can be used when you need to to select a certain object from a collection like a condition.
Your development process should always be in continuous refactoring. Any changes to the matter you talk with your Experts should be immediately represented in code. If you see that some term starts occur too often just create an Entity
for it - bring it to the Light.
Preserving Model Integrity
Imagine a big enterprise project. Could you imagine it with one model which is understandable for whole team, say 200 people? Most commonly such big projects are divided to smaller teams and which of them works on own model. So whole project consists of couple of models and some of them are intersected with other models, some of them uses other models and all of this is the Integrity
of your Models.
Each model has a context. When we deal with single model, the context is implicit. There is no need to define it. When we create an application which is supposed to interact with other software, for example a legacy application, it is clear that old application has its own model and context, and they are separated from the legacy model and its context. They cannot be combined, mixed or confused. When we work on enterprise project we need define the context of each model, we create. Also a model should be small enough to be assigned to one team. Only this way we could keep knowledge up to date and integrated into system.
Whole project should be assembled continuously using Continuous Integration.
Iterations between different contexts
You could probably say that you cannot have 100 models in your project without intersections between them. And you are right. Eric Evans sees few variants of such intersections.
The purpose of shared kernel is to reduce duplication, but still keep two separate contexts. Designate some subset of the domain model that the two teams agree to share. Of course this includes, along with this subset of the model, the subset of code or of the database design associated with that part of the model. This explicitly shared stuff has special status, and shouldn’t be changed without consultation with the other team.
There are times when two subsystems have a special relationship: one depends a lot on the other. You could imagine core functionality system and reporting system. Probably reporting system depends on the core one. In such case it is very and very important that team which works on core will be supplying reporting team. That is simply needed.
This is much like the Shared Kernel
, but there is an important difference. The customer team cannot make changes to the kernel. They can only use it as part of their model, and they can build on the existing code provided.
We should build an Anticorruption Layer which stands between our client model and the external one. This layer works as a two way translator between two domains and languages. This layer could be needed when we deal with legacy/external model and have no ability to do changes to it.
Open Host Service
The solution is to see the external subsystem as a provider of services. If we can wrap a set of Services around it, then all the other subsystems will access these Services, and we won’t need any translation layer. The difficulty is that each subsystem may need to interact in a specific way with the external subsystem, and to create a coherent set of Services may be problematic.
One of the key requirements to your Infrastructure to be able deal with DDD is Persistence Ignorance
. This means that once you have your Infrastructure
setuped you no longer need to care about how your data goes to database and how it is saved there. Also it is commonly used with Unit Of Work Pattern
. This could be accomplished with some of the ORM frameworks like Hibernate
How do we classify ORMs? We look on the ways how it solves different problems which occur when you map you object-oriented data to relational database, but yea.. there are ORMs which map to the Object databases. But the most common variant is when you have Relational database. So wee take a look how ORM resolves Inheritance issues, what is the querying language of it, does it support all common databases operations like aggregating, ordering, grouping, etc.
Basically all ORMs has Identity Map
strategy they allow us work using Unit Of Work
pattern and they could provide Lazy Load
to do not hit database often.
is the ORM which all the features required to quickly build an advanced persistence layer in your code.
Patterns and practices compatible with Domain-Driven Design
Nowadays Agile is a way to develop software and DDD is very comfortable with it and with technics of the Agile like TDD.
is very good thing to use with DDD. Why? Because writing test before means that your are describing your model just like it was described on the meeting with your Experts. There are even enthusiasts who wants to rename TDD to BDD (Behavioral-Driven Design).
Advantages for design
- More client control
- More Interfaces and Fabrics
- More concrete
- Low coupling
- Another version of API
- Long term support
- Like smart compiler
- UI & Database testing
- You feel too much safe
- Possilitity to lose whole visibility
- More code to maintain
While designing your architecture with DDD you often uses such patterns or approaches like:
Generally speaking, SOA is a flexible set of design principles used during the phases of systems development and integration. It provides set of Services with which you are working.
In computing, aspect-oriented programming (AOP) is a programming paradigm in which secondary or supporting functions are isolated from the main program's business logic. It aims to increase modularity by allowing the separation of cross-cutting concerns, forming a basis for aspect-oriented software development.
(Inversion of Control and Dependency Injection
Inversion of Control
is principle with which you could easily resolve your dependencies in code delegating setup to some other part like IoC framework (StructureMap
For the end I want to mention that my story is based on books by the link below.
I read all of them (except of duplicated ones
) and recommend you to read them also. This will bring a lot of interested stuff to your mind. For example I could state that now I understand better the design of subsystem with which I work on enterprise project. Future is for Domain-Driven Design.
P.S. I hope that you enjoyed my story. Do not hesitate to leave your comments.