Introduction
When we start a new project, sometimes we are assigned to estimate times for our works. Generally, we try to make some small tasks from the requirement and estimate our time and prepare our sprint plan. But can we break our tasks small enough to estimate our time? In case of large complex project, it sometimes becomes almost impossible to estimate time because of lack of proper knowledge on domain of the project. In those cases, we estimate our times and then suddenly understand it is not enough. Reverse is also true, sometimes we overestimate because we don’t know what we actually need to do. There are also some cases that developers develop something and then suddenly realize that they don’t need it. Also in most of the cases, our code and also the language of the code does not reflect the business or domain. So after few days, it becomes almost impossible to understand what the code actually does though it seems to us initially that we use proper naming convention. We can easily eliminate these problems by following domain driven design that reflects the domain of the business in the code and makes it more maintainable and understandable even to those who are not developers or software engineers.
Topics Discussed
- Domain
- Domain Model
- Ubiquitous Language
- Entities
- Value Objects
- Aggregates and Aggregate Roots
- Bounded Context
- Layers in DDD:
- Repository
Domain
To understand domain driven design, first we need to understand what the domain of our application is. According to Eric Evans,
Quote:
Every software program relates to some activity or interest of its user. That subject area to which the user applies the program is the “domain” of the software”.
Suppose, you are assigned to build a shop management system. We need to consider, is the term “Shop Management” representing our domain correctly? Some may think yes. But is it really? Because if you are going to build a system for super shop, it will be different from a grocery shop or Men’s/ females’ wear shop. So we have to be specific. We need to tell that we are going to build a super shop management system. That will make more sense than a general term shop management system. Another example can be a building management system where it would be more appropriate resident building management system or commercial building management system.
Domain Model
A domain model is a diagram that will reflect the domain of the application. The domain model should hold the total idea of the domain and after seeing it anyone should understand what it means. If a software developer is told to develop an application about air plane traffic routing, it would be impossible for him to develop an application without the knowledge of the domain. But unfortunately, software developer does not know anything about air control. So who knows about it? The person who is controlling the air traffic, he is an expert of it and he knows about it clearly and it is impossible for the developer to develop an application without the help of the air plane traffic controller. Here I am giving an example with a Lease Application for Flat. Here with DE, I will indicate domain expert and with SD software Developer.
DE: I need a lease application form for giving lease of the flats of my apartment.
SD: Ok no problem. So what information a lease applicant should fill up?
DE: There are different types of information a lease Applicant may need to fill up. First of all Lease Applicant should fill up all his personal details, then his financial information, then about a person with whom we can contact in any case of emergency.
SD: Ok Let’s say the person with whom you can contact in times of emergency as Emergency contact. Is it like this?
DE: Alright. But this is not the end. There are also co-applicants. Co Applicants need to fill up the same field as primary applicant. But they need to send accept request to the Lease applicant and lease applicant need to accept that request.
SD: I get it completely. So it look like this.
But I have a question. How the current system works for the co-applicant. Is there any kind of Id with which you can identify that this co-applicant form is associated with that Lease Application form?
DE: Oh, yes! There is a unique application number and 4 digit pin number associated with every Lease Application form. The main lease applicant is provided with number of co applicants’ form upon his request. The application number is printed in the co-applicants’ form as well as lease applicant’s form. But the pin number is only printed in lease applicant’s form. He needs to tell the pin number to the co-applicant. When co-applicant submit the form we match both the application number and pin number to avoid fraudulence.
SD: So, in our system, there will be a lease application form with a unique application number. There will be also a pin number that will be generated by a pin number generator and we will add the pin number with the main lease applicant form only. And the co-applicants will be attached to that lease application after they provide the valid pin number and fill up all required information. So the final system looks like this:
DE: Yes it is.
This is the small example for domain model. The domain model can also have all the property/ attribute names in it. Generally, Real world project scenarios are more complex than my example. One thing here is that I didn’t use any specific diagram. I just draw a diagram that may help me and my team to understand the domain. But with UML diagrams, it is not possible to properly visualize the domain of the application. So the domain model may be any diagrams, a text or even pictures that reflects the domain. According to Eric Evans,
Quote:
The domain model is not a particular diagram; it is the idea that the diagram is intended to convey. It is not just the knowledge in a domain expert’s head; it is a rigorously organized and selective abstraction of that knowledge. A diagram can represent and communicate a model, as can carefully written code, as can an English sentence.
What is in our domain model, it should be in the domain of the software. If a thing does not exist in the domain model, it shouldn’t be the part of the domain of the software
Ubiquitous Language
Domain experts may not know anything about software development. If a software developer talks to the domain experts in their language, domain experts may not understand anything.
Example
in the previous conversation if the software developer tell the domain expert something like this:
SD: So, in our system, there will be a lease application form with a unique application number. The number may be a guid. There will be also a pin number that will be generated by a pin number generator and we will add the pin number with the main lease applicant form only. When the co applicants give the correct input for pin, we will set a flag as validcoapplicant and then the application will be attached to the lease applicant’s.
This language will be hard to understand for any person other than who knows about coding and understand what guid and flag means. In the same way, sometimes the language of domain experts may become difficult to understand for the developer. So the domain experts and the developers need to find a common language that they both understand. It is not easy, it may take time to develop the language but it is extremely necessary. The domain model should provide the backbone of the language. This language should be used by the developers and domain experts even to communicate among themselves. As the domain model provides the backbone of the ubiquitous language, this language will also be reflected in the code. One of the biggest advantage of using ubiquitous language among team (Here team means system analysts, developers, domain experts all the person related to the project) is as the language is repeatedly used among team members, it will help us to identify the weakness of the domain model and help to fix it quickly. If anything changes in the ubiquitous language, it means it changes in the domain model as well as in the code. We may also say if anything changes in the domain model, it changes in the ubiquitous language and also in the code. If there is no changes in your code though the language changed, it means you are not following DDD properly because domain model is what that is reflecting in your code.
Entities
To tell simply, Entities are objects with conceptual identity. They have the identity so that we can track and distinguish them with their identity. An entity should not have only getters and setters rather it should encapsulate all of its behavior means it can have methods that is related to its behavior or indicates its behavior. Entities are mutable. We can change entities’ attribute without changing its identity but we don’t need to destroy the whole entity for it.
Example
From the diagram, Lease Applicant and Co-Applicnat are an entities. Because Lease Applicant has a conceptual id and we need to track the Lease Applicant and maintain its persistence and Co-Applicant needs to send accept request to lease applicnat and so we need to identify them uniquely and they should have an id. But Emergency Contact, Financial Information and Personal Detail is not an entity because they are part of the lease applicant and co applicant and we don't need to identify them separately.
Value Objects
There are some objects that have no conceptual identities. These objects are called value objects. We must remember that we are not talking about Id attribute of our database rather we are discussing about an id that an object really needs. Eric Evans told regarding value objects,
Quote:
“VALUE OBJECTS are instantiated to represent elements of the design that we care about only for what they are, not who they are.”
Value objects are immutable. So they cannot be changed. We don’t care about any specific instance of value objects. If we need to delete a value object we will destroy the whole object and create a new one. It should be noted that one system’s value objects may become an entity and an entity may become to value objects in another system depending on different application requirements.
Example
Emergency Contact, Personal Information and Financial Information are value objects as they don't need an identity. We can make value objects immutable by making the setters private.Here is an example.
Another example, suppose you have a blog and you posted on high quality topics of software development. Many people likes your post and they commented on the posts. Here post is entity, because we need to track every post separately but comments are value objects because they don’t have any conceptual identity because we don't need to identify a comment separately.
Anemic Domain Model
Anemic domain model is an anti pattern. In OO Design an object should hold all the behaviors of it. But sometimes, we create entities with just some getters and setters and create a corresponding service object which contains other behaviors like methods and delegates. We generally use a reference of the entity in that service object. But according to object oriented design principles, an object should encapsulate all of its behavior which we call encapsulation. When we are creating a separate service class or object, we are clearly violating this principle thus violating SOLID principle also. One of the characteristics of anemic domain model is at first glance, it looks perfect because we name the class with nouns and use verbs for the method but the catch come when we try to analyze it its behaviors as sometimes it does not possess any behavior or contain a mix of behavior and also sometimes contain partial behavior as another object has some of its behavior. According to, Single Responsibility Principle,
Quote:
An object should have only one reason to change.
If an object contain multiple behaviors or no behaviors or partial behaviors, the object may change for many reason. So it violates Single Responsibility Principle.
Example
We have classes named LeaseApplicant, CoApplicant, EmergencyContact, PersonalDetail and FinancialInformation. Suppose we have two service class CoApplicantService and LeaseApplicantService. CoApplicantService has a method called SendAcceptRequest and LeaseApplicantService has a method called AddCoApplicant. This makes our domain model anemic because SendAcceptRequest is the characteristics of CoApplicant and it should be in the CoApplicant class, not in a separate Service class. AddCoApplicant is the behavior of LeaseApplicant, so it should be in the LeaseApplicant class not in LeaseApplicnatService.
Aggregates and Aggregate roots
Aggregate is a single unit consists of entities and value objects. The root entity of the single unit is called aggregate root. An aggregate can hold the reference of another aggregate.
Example
In the examples we talked about earlier Lease Applicant, Co-applicant, Financial Information, Emergency Contact and Personal Detail form an aggregate and Lease Applicant is the root entity hence the aggregate root because without it others don’t bear any value. Anything outside the aggregate can only reference the aggregate root. That means we can only reference Lease Applicant from outside the boundary of aggregate. We don’t have any access to Co-applicant, Financial Information and others from outside the aggregate boundary. If we need to perform any operation on these, we have to perform it inside the aggregate boundary. Thus the aggregate will help us to maintain transactional integrity.
Bounded Context
A large system has separate parts consisting of some common concepts in those parts. If we think about an ecommerce system, we may find two essential parts: one is gathering products from different vendors and sales center and another is selling products and managing online transaction. These are two different bounded context.
Layers in DDD
I take the picture from Eric Evans book on DDD because, I think describes the layers of DDD best.
Services in DDD
Domain Service
Domain Service generally consists of those that did not fit well in domain objects. Generally these are not CRUD operation. We ofcourse should try to fit our actions into the entities but there are some occasions that some specific operation does not fit into the domain. In these situations, domain services come into play. In the conversation I gave above, Pin Number Generator is a domain Service. Another example is fund transfer of bank between two accounts. Fund Transfer does not really fit in the Account object and it needs a service. Another example can be the calculation of CGPA of student. CGPA Calculation is not really part of the student so it needs a domain service.
Infrastructure Service
Infrastructure services are generally for database or file access, sending email. These are not part of the domain and these does not exist in ubiquitous language and domain model. The domain layer doesn’t care how the data is saved or any notification is sent. This layer is completely technical.
Application Service
Application layer generally exposes the functionality of domain in other application layers and provides necessary information to show in the UI. Sometimes domain layer needs access to database or other apis to complete a business logic. We may perform that task in entity but that will violate the SOLID principles. The best solution is to use the application layer to fetch the data and provide necessary data to domain layer to perform the operation. Suppose, we need to calculate the yearly revenues for a company. The Application layer will make a call to database to get all the revenue related information and pass it to domain layers to calculate the revenues.
Repositories
So far we have created a persistence domain model and now we need to save and retrieve the data from database or any other kind of IO storage. Repository provides us this way. But we should keep in mind that repository is not a data access layer. Martin Fowler states that,
Quote:
“Mediates between the domain and data mapping layers using a collection-like interface for accessing domain objects.”
So we may create a generic repository and add aggregate root or collection of aggregate root to it. We can’t add value objects to repositories as we can only reference aggregate root from outside of aggregates.
References