Domain-Driven Design (DDD) is a concept of developing software that the structure of the codebase deeply matched the business domain.
For more information about DDD, you can find it here
It took me pretty much time to understand and apply it to daily tasks, so this article is my sharing all I know about DDD.
That rule specifies that something declared in an outer circle >must not be mentioned in the code by an inner circle.
--- The Clean Architecture - Uncle Bob ---
Dependency is the only thing we can't avoid in software development because a component/class either uses another component/class or is being used by another one. But we can control the direction of dependency.
Let say we have a chain A -> B -> C
- A depends on B, B depends on C, then C depends on A. So if C changes, B has to change, which leads to A has to change. We call this circular dependency, which is always a bad idea.
- We can control this direction of dependency by Dependency Inversion. Adding additional component A', we have the new chain A -> B -> C, C -> A', A -> A'. Now, everything depends on A'. In this case, C may change, A may change, but when A' is the same, the changes won't affect others.
- A' could be an interface in the context of Java language, and domain at the module level.
- Description: represent the entry point of requests.
- Middleware (Request validation, error handler).
- Build response.
- Description: call request to external resources (external API, Database, Message queue ...).
- Middleware (Request validation, authentication, error handler) for external API.
- Manage DB connection.
- External logic for main business one.
- Description: contain orchestration/process logic.
- Handle business flow.
- Make external calls to retrieve data.
- Description: contain shared logic across process, mainly focus on a specific domain.
- validation for business logic.
- implement business logic.
It's too much for theory, now come back to reality!!
Let assume we have a business flow below, which is about booking a seat in a cinema:
- IsInvestor: We don't want to cause any trouble for investors, so if they ask for a seat, we will take it for them.
- IsEligible: in this step, we will check whether the user's age is old enough.
- Register seat: register the user for the seat.
- Notification: send email to let them know whether the request is success or fail.
Now we structure our codebase from outer layer to inner layer based on the onion model above.
Controller: This layer handles 3 things
- Mapping router to the controller.
- Validate required data for the request.
- Get suitable business flow in the Application layer to handle this request
Repository: This layer makes requests to external services. In this case, we may need user information from User downstream, seat information from Seat database.
Domain: this layer handles business logic about User, Seat, and Email service. There is a lot of work to do here:
- Retrieve required data for business logic from downstream.
- Return result of isInvestor, isEligible, and so on.
After all all codebase will look like:
├── controller | └── register-seat | ├── registerSeat.controller.ts | ├── registerSeat.router.ts | └── registerSeat.validator.ts ├── application | └── register-seat | └── registerSeatService.ts ├── repository | └── email | | ├── type.ts | | ├── emailBuilder.ts | | └── emailService.ts | ├── user | | ├── type.ts | | └── userService.ts | └── seat | ├── type.ts | ├── seatSchema.ts | └── seatService.ts └── domain ├── user | └── eligibility | ├── isSeatAvailable.ts | └── isCurrentUserOldEnough.ts ├── email | └── sendEmail.ts └── seat └── seatInfoProvider.ts
Note: sub-domains name may have different of mine, depended on project business domain.
This article is all thing I know about DDD, I may misunderstand some points, please help me fix it by leaving your feedback, I am really happy. Thanks for your precious time reading this.