Domain-Driven Approach Towards Software Design
Have you ever struggled with the Software Design or Do you find it difficult? If your answer is yes, you are NOT ALONE. Many of us face the same problem with the design. Most of the time, the complexity of the system and the enormity of the task in hand overwhelm us. This article will help you develop a simplified and methodical approach to software design.
We seldom get a perfect and finalized design in the first iteration itself. No matter how hard we try, every software design will go through multiple iterations. But, these iterations should not entirely change the design. Obviously, it will add new entities, modify and remove some existing entities. But, if one has to redesign everything from scratch, it’s been messed up pretty badly.
With time, when the system grows, it brings more complexity to it. With each newly added feature, it becomes hard to keep the system organized, clean and adhered to the guidelines. For such highly complex systems, Domain-Driven design is a recommended approach.
“Domain” in domain-driven design is the business logic, it is the heart of the software design. As the name aptly suggests, all the entities in this paradigm revolve around domain knowledge. In this paradigm, design start with domain entities and gradually expands to other layers in the system.
This design style divides entities into four different layers
- Presentation Layer handles user interface responsibilities. It depends on the application layer to execute user action.
- Application Layer has the entities which are exposed to the outside world like rest controllers, VOs, classes that keep track of tasks progress, etc. This layer defines job/action which uses multiple domain layer services, entities, etc. to perform user action. This layer handles application security.
- Domain Layer entities encapsulate the business logic of the system. It has services, repositories, etc.
- Infrastructure Layer contains all the implementation details of repositories that handle persistence, inter-node communication, caching, etc.
In this design style, our starting point is domain entities with main focus on business logic. Step by step, we expand. Get into peripherals. Give our business logic a persistence, caching ( i.e. Infrastructure Layer). Then, we add security to the application through the Application Layer. We define user tasks using domain entities, design a user interface to invoke these tasks (Presentation Layer). Block by block, layer by layer, we design the system.
Enough concepts, for now, let’s see how this works with an example, will come back to more concepts later.
Design a parking space system. It has multiple floors with multiple entries and multiple exit points. A customer will get the ticket at entry and will pay at the exit. Customers can pre-book parking slots. Admin also can keep some slots reserved.
Let us divide it into epics, epics would be as follows.
- Parking space capabilities like adding parking floors, a new vehicle type parking, etc.
- Customer options to pre-book parking, getting a ticket, a pass.
- Payment options for customers
- Admin capability to reserve some slots, change parking ticket prices, issue passes.
- Entry console capability to scan a ticket.
- Exit console capability to scan and output the total payable amount
Now, let us divide these into user stories.
Writing fine-grained user stories is a foremost step towards developing a great software.
Breaking down epics into user stories is a very important step, not just in this but any design style. Writing down each requirement as a user story helps us define all the possible system interactions. This will make us not to miss out on any system requirement.
User stories are like children, they both need your time and attention.
Above epics can be broken down into user stories as follows
1. Admin Capabilities
- Parking Space should provide admin registration capability.
- As an Admin, I should be able to add/remove the parking floor to the parking space.
- As an Admin, I should be able to add/remove vehicle type from the parking space.
- As an Admin, I should be able to add/remove parking attendant from the parking space.
- As an Admin, I should be able to add/remove entry and exit points to the parking space.
- As an Admin, I should be able to issue a pass to a customer.
- As an Admin, I should be able to suspend an issued pass.
- As an Admin, I should be able to change parking rates.
- As an Admin, I should be to add/remove new payment options.
- As an Admin, I should be able to reserve some parking slots for some groups.
2. Parking Attendants
- As a Parking Attendant, I should be able to scan the ticket at the exit and get the total payable amount.
- As a Parking Attendant, I should be able to make some parking slots available /not-available for some vehicle types.
- As a Parking Attendant, I should be able to suspend/resume parking on floors.
3. Customer interaction
- As a Customer, I should be able to get the ticket at the entry for a vehicle.
- As a Customer, if I have a pass, I should be able to scan pass at entry and should be allowed to park.
- As a Customer, if total reserved parking is full, I should be able to get the ticket.
- As a Customer, I should be able to pay at any exit.
- As a Customer, I should be able to pre-book a slot for a day/week/month.
Extract Nouns and Verbs
Now, its time to list down all Nouns and Verbs from the user stories.
- Parking Space
- Parking Slot
- Parking Attendant
- Vehicle Type
- register admin
- add/remove parking floors
- add/remove vehicle types
- add/remove parking attendant
- add/remove entry/exit points
- get the ticket
- pay at exit
- pre-book a slot
- reserve a slot
- add/remove/disable payment option
- change parking rates
- register/de-register user
- scan ticket
Object Interaction Diagram
We have noted down nouns and verbs. Let us draw an Object Interaction Diagram using these.
Object Responsibility Diagram
Now, let us add responsibilities to appropriate objects and create an Object Responsibility diagram from it.
Let us create a class diagram using an object interaction diagram.
Before starting with a class diagram, one should always keep in mind that every business logic should be behind an interface.
When we design like this, by identifying entities, adding responsibilities to them, separating concerns from the first step itself i.e. object interaction diagram, many of the design principles get followed.
A class diagram would look like as follows
This class diagram is created with the Creately.com
Domain-Driven architecture has different entities, like Value Objects, Services, Entities, Repositories, Aggregates.
In this example, we have not covered the presentation layer
- Controllers that will receive user action are part of the application layer (not present in this diagram).
- Controllers like ParkingAttendantController will use domain layer service ParkingAttendantService to perform a user action of managing ParkingAttendant.
- Domain layer service would create composite objects from entities called aggregate and persist it using repository like ParkingAttendantRepository. Repository definition is part of the domain layer but implementation lies with the infrastructure layer.
- The infrastructure layer implements domain layer repositories. This layer could implement first-level caching of aggregates for faster retrieval. This decision of speeding up a retrieval using caching or some alternate mechanism completely lies with the infrastructure layer.
Thus, the interaction between these 4 layers looks as follows
Presentation -> Application -> Domain <- Infrastructure
Division of responsibilities between these 4 layers guards domain logic against polluting it with unnecessary classes and reduces its complexity. This makes maintenance a less painful task.
I am using this software design style for a while now. I find it the most methodical and simplified approach to design and maintain complex software. I hope you learned something and this will help you in your future software designs.
Let me know your thoughts and comments.
Thanks for reading this.