Domain-Driven Design (DDD) is a software design approach introduced by Eric Evans. It focuses on modeling software around the business domain rather than technical implementation details.
Simply put:
DDD = Understand the Business First, Then Write the Code
Instead of asking:
"Which database table should I create?"
DDD asks:
"How does the business actually work?"
Why DDD?
Imagine you are building an Internet Banking System.
Traditional Approach:
UI
↓
Business Logic
↓
Database
Problems:
Business logic scattered everywhere
Difficult to maintain
Hard to test
Database drives design
Tight coupling
DDD Approach:
Business Domain
↓
Application Layer
↓
Infrastructure
↓
Database
Business rules become the center of the application.
Real-World Project
Let's build:
Online Banking System
Features
Customer Registration
Account Creation
Deposit Money
Withdraw Money
Transfer Money
Loan Request
Transaction History
Large organizations using similar concepts:
HDFC
ICICI
SBI
PayPal
Stripe
Step 1: Understand the Business
Business experts explain:
Customer owns Accounts.
Each Account has Balance.
Customer can transfer money.
Transfer must:
Check sender balance
Deduct sender balance
Credit receiver
Record transaction
Send notification
Notice:
No one mentioned SQL Server.
No one mentioned Entity Framework.
Because DDD starts from business.
Step 2: Identify Domains
Banking System
├── Customer
├── Accounts
├── Loans
├── Payments
├── Notifications
├── Authentication
Each becomes its own domain.
Step 3: Identify Bounded Contexts
A bounded context defines where a particular model is valid.
Example:
+----------------------+
| Customer Context |
+----------------------+
Customer
Address
Profile
KYC
Another context:
+----------------------+
| Banking Context |
+----------------------+
Account
Balance
Transaction
Transfer
Loan Context:
Loan
EMI
Interest
Approval
Authentication Context:
Login
JWT
Refresh Token
Permissions
Each context is independent.
Complete Architecture
Banking System
+---------------------------------------+
Customer Context
Banking Context
Loan Context
Payment Context
Notification Context
Identity Context
+---------------------------------------+
Each can become its own Microservice.
DDD Layers
Presentation
↓
Application
↓
Domain
↓
Infrastructure
Let's understand each.
1. Presentation Layer
Contains
ASP.NET Core API
MVC
Angular
React
Example:
POST
/api/account/transfer
No business logic.
Only:
Receive request
↓
Call Application Layer
2. Application Layer
Responsible for
Use cases
Coordination
Transactions
Example
TransferMoneyCommand
TransferMoneyCommand
↓
TransferMoneyHandler
↓
Domain
↓
Repository
↓
Save
No business rules here.
Only orchestration.
3. Domain Layer (Heart of DDD)
Contains:
Entities
Value Objects
Aggregates
Domain Events
Interfaces
Business Rules
Everything important lives here.
Example Entity
Account
Id
AccountNumber
Balance
CustomerId
Methods
Deposit()
Withdraw()
Transfer()
Notice
Not
Balance = Balance - amount
from Controller.
Instead
Account.Withdraw(amount)
Business logic belongs inside entity.
Example
public class Account
{
public decimal Balance { get; private set; }
public void Deposit(decimal amount)
{
if(amount <=0)
throw new Exception("Invalid amount");
Balance += amount;
}
public void Withdraw(decimal amount)
{
if(amount > Balance)
throw new Exception("Insufficient funds");
Balance -= amount;
}
}
Business rule:
Cannot withdraw more than balance.
Lives inside entity.
Value Objects
Example:
Address
Street
City
State
ZipCode
Identity doesn't matter.
Two identical addresses are equal.
Example:
Address A
=
Address B
Value Objects are immutable.
Another example:
Money
100 INR
instead of
decimal
Aggregate
Aggregate groups related objects.
Example:
Customer
↓
Accounts
↓
Transactions
Customer is Aggregate Root.
Only root is accessed directly.
Example
Customer
↓
Savings Account
↓
Current Account
↓
Transactions
Don't modify child objects directly.
Go through Aggregate Root.
Repository Pattern
Instead of
DbContext.Accounts.First()
Use
IAccountRepository
Example
public interface IAccountRepository
{
Task<Account> GetById(Guid id);
Task Save(Account account);
}
Infrastructure implements it.
Infrastructure Layer
Contains
SQL Server
EF Core
RabbitMQ
Azure Service Bus
Redis
Email
Azure Storage
Nothing business-related.
Example
AccountRepository
↓
Entity Framework
↓
SQL Server
Domain Events
Suppose money transferred.
Need to
Send Email
SMS
Push Notification
Audit Log
Don't call everything directly.
Instead raise event.
MoneyTransferredEvent
Event
↓
Notification Service
↓
Email Service
↓
Audit Service
↓
Analytics
Loose coupling.
Example
public class MoneyTransferredEvent
{
public Guid FromAccount { get; set; }
public Guid ToAccount { get; set; }
public decimal Amount { get; set; }
}
CQRS with DDD
Separate
Commands
Deposit
Withdraw
Transfer
Queries
Get Balance
Get Transactions
Get Customer
Never mix.
Architecture
API
↓
Command
↓
Handler
↓
Domain
↓
Repository
↓
Database
Queries
API
↓
Read Database
↓
DTO
Very scalable.
Folder Structure
BankingSystem
│
├── Banking.API
├── Banking.Application
│
├── Banking.Domain
│
├── Banking.Infrastructure
│
├── Banking.Shared
│
└── Banking.Tests
Inside Domain
Domain
│
├── Entities
├── ValueObjects
├── Events
├── Repositories
├── Aggregates
├── Exceptions
├── Enums
Application
Application
│
├── Commands
├── Queries
├── DTOs
├── Interfaces
├── Handlers
Infrastructure
Infrastructure
│
├── Persistence
├── Repositories
├── Messaging
├── Azure
├── Email
├── Redis
API
Controllers
Program.cs
Middleware
Swagger
Filters
Transfer Money Flow
Angular
↓
Transfer API
↓
Transfer Command
↓
Command Handler
↓
Account Aggregate
↓
Withdraw()
↓
Deposit()
↓
Raise Event
↓
Repository
↓
SQL Server
↓
Notification
DDD + Microservices
Customer Service
↓
Account Service
↓
Loan Service
↓
Payment Service
↓
Notification Service
Each service owns its own database and domain model.
Advantages
Business-focused design
Clear separation of concerns
Easier maintenance
Easier testing
Highly scalable
Fits microservices well
Encourages rich domain models
Better communication with domain experts
Reduces duplicated business logic
Challenges
Steeper learning curve
More upfront design effort
Can be overkill for small CRUD applications
Requires close collaboration with business experts
More classes and abstractions than simple layered architectures
Common Interview Questions
1. What is Domain-Driven Design?
A software design approach that models software around the business domain, placing business rules in the domain model rather than spreading them across controllers or data access code.
2. What is a Bounded Context?
A clearly defined boundary within which a specific domain model and vocabulary are valid. Different bounded contexts can have different models for the same real-world concept.
3. What is an Entity?
An object defined by its identity that can change over time, such as a Customer or Account.
4. What is a Value Object?
An immutable object defined by its values rather than identity, such as Money or Address.
5. What is an Aggregate?
A cluster of related entities and value objects treated as a single consistency boundary, with an Aggregate Root controlling access.
6. What is an Aggregate Root?
The main entity of an aggregate through which all changes to the aggregate are made, ensuring business invariants are maintained.
7. What is a Repository?
An abstraction for loading and saving aggregates without exposing persistence details.
8. What are Domain Events?
Events raised by the domain to signal that something important has happened (for example, MoneyTransferredEvent), allowing other parts of the system to react without tight coupling.
9. How does DDD work with CQRS?
Commands change state through the domain model, while queries read optimized data models. This separation improves scalability and keeps business logic focused.
10. When should you use DDD?
DDD is most valuable for complex business domains—such as banking, insurance, healthcare, logistics, and e-commerce—where business rules are rich and evolve over time. For simple CRUD applications with minimal business logic, a traditional layered architecture is often sufficient.
Summary
In the banking example, DDD helps you model concepts like Accounts, Customers, Money Transfers, and Transactions the way the business understands them. Business rules such as "an account cannot be overdrawn" live inside the Account aggregate, application services coordinate use cases, repositories abstract persistence, and domain events notify other services of important changes. This leads to software that is easier to evolve, test, and scale as business requirements grow.