Entity vs Domain Model vs ViewModel vs DTO
| Type | Main purpose | Common layer | Contains |
|---|---|---|---|
| Entity | Represents data stored in the database | Domain/Infrastructure | Properties, primary key and relationships |
| Domain Model | Represents business concepts and rules | Domain | Business data, behaviour and validation rules |
| ViewModel | Provides data required by a specific View | MVC presentation | Display fields, form fields and UI validation |
| DTO | Transfers data between layers, APIs or applications | Application/API | Only the required transferable data |
1. Entity
Usually represents a database record and has an identity.
public class Product
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public decimal Price { get; set; }
}
2. Domain Model
Represents business behaviour and protects business rules.
public class Order
{
public decimal TotalAmount { get; private set; }
public void ApplyDiscount(decimal percentage)
{
if (percentage is < 0 or > 50)
throw new ArgumentException("Invalid discount.");
TotalAmount -= TotalAmount * percentage / 100;
}
}
An entity can also be a domain model. In a rich domain design, entities contain both identity and business behaviour.
3. ViewModel
Designed specifically for displaying or receiving data in an MVC View.
public class ProductViewModel
{
public string Name { get; set; } = string.Empty;
public string FormattedPrice { get; set; } = string.Empty;
public List<string> Categories { get; set; } = [];
}
4. DTO
Transfers selected data between an API and its clients or between application layers.
public class ProductDto
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public decimal Price { get; set; }
}
Simple Flow
Database
↕
Entity / Domain Model
↕
Service
↙ ↘
ViewModel DTO
MVC View API Client
Key Points
- Entity: Database identity and persistence.
- Domain model: Business concepts, behaviour and rules.
- ViewModel: Data shaped specifically for an MVC View.
- DTO: Data shaped for transfer between boundaries.
- Do not expose database entities directly to Views or API clients.
- ViewModels and DTOs help prevent overposting and hide sensitive fields.
- Mapping can be performed manually or with a mapping library.
Why Should Database Entities Not Be Passed Directly to Views?
Database entities represent the database structure. Views usually require data shaped specifically for the user interface. Therefore, pass a ViewModel to the View instead.
Main Problems
- Security risk: Sensitive properties may be exposed accidentally.
- Overposting: Users may submit values for fields they should not modify.
- Tight coupling: Database changes directly affect the View.
- Unnecessary data: The View may receive more fields than required.
- UI-specific requirements: Display formatting and dropdown lists do not belong in an entity.
- Validation differences: Database rules and UI validation rules may differ.
- Maintainability: Entity-based Views become harder to change and test.
Example Entity
public class User
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public string PasswordHash { get; set; } = string.Empty;
public bool IsAdmin { get; set; }
}
Passing this entity directly could expose or bind sensitive fields such as:
PasswordHash
IsAdmin
Recommended ViewModel
public class EditUserViewModel
{
public int Id { get; set; }
[Required]
public string Name { get; set; } = string.Empty;
}
Controller
public IActionResult Edit(int id)
{
var user = _context.Users.Find(id);
var viewModel = new EditUserViewModel
{
Id = user!.Id,
Name = user.Name
};
return View(viewModel);
}
Key Points
- Entities are designed for database persistence.
- ViewModels are designed for UI display and input.
- ViewModels expose only the required fields.
- ViewModels help prevent overposting but must still be mapped safely.
- Never trust posted identifiers or authorization-sensitive values; reload the entity and update only permitted fields.
What Is a Domain Model?
A domain model represents a real business concept along with its business rules and operations.
Here, domain means the business area of the application.
Examples:
| Application domain | Domain models |
|---|---|
| Banking | Account, Transaction, Loan |
| Shopping | Product, Order, Customer |
| Pension | Member, Contribution, Benefit |
| Hospital | Patient, Appointment, Prescription |
Simple Banking Example
A bank account has data:
- Account number
- Balance
It also has business rules:
- Deposit must be greater than zero.
- Withdrawal cannot exceed the available balance.
public class BankAccount
{
public int Id { get; private set; }
public decimal Balance { get; private set; }
public void Deposit(decimal amount)
{
if (amount <= 0)
throw new ArgumentException(
"Deposit amount must be greater than zero.");
Balance += amount;
}
public void Withdraw(decimal amount)
{
if (amount <= 0)
throw new ArgumentException("Invalid amount.");
if (amount > Balance)
throw new InvalidOperationException(
"Insufficient balance.");
Balance -= amount;
}
}
Here:
Balance → Business data
Deposit() and Withdraw() → Business operations
Validation inside methods → Business rules
Entity vs Domain Model
| Entity | Domain Model |
|---|---|
| Mainly represents stored data | Represents the business concept |
| Usually contains properties | Contains data, behaviour and rules |
| Often follows a database table | Follows business requirements |
| May allow properties to be changed directly | Protects data through business methods |
Simple Entity
public class BankAccountEntity
{
public int Id { get; set; }
public decimal Balance { get; set; }
}
Anyone can incorrectly change the balance:
account.Balance = -50000;
Domain Model
account.Withdraw(5000);
The domain model verifies the business rules before changing the balance.
Important Clarification
An entity and domain model do not always have to be separate classes.
In a well-designed application, the same class can be both:
- An entity because it has a unique identity such as
Id. - A domain model because it contains business behaviour and rules.
Key Points
- A domain model represents the business, not the screen.
- It contains business data and business behaviour.
- It prevents invalid business states.
- Controllers should call domain or service methods instead of directly changing important values.
- A model containing only public properties is commonly called an anemic domain model.
- Domain models are especially useful for applications with complex business rules.
How Does Model Validation Work?
Model validation checks whether submitted data follows the validation rules defined in the model.
Validation Flow
Form Submission
↓
Model Binding
↓
Validation Attributes Checked
↓
Errors Added to ModelState
↓
Controller Checks ModelState.IsValid
ViewModel
public class ProductViewModel
{
[Required(ErrorMessage = "Name is required.")]
[StringLength(100)]
public string Name { get; set; } = string.Empty;
[Range(1, 100000)]
public decimal Price { get; set; }
}
Controller
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Create(ProductViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
// Save valid data
return RedirectToAction("Index");
}
View
<form asp-action="Create" method="post">
<div asp-validation-summary="All"></div>
<input asp-for="Name" />
<span asp-validation-for="Name"></span>
<input asp-for="Price" />
<span asp-validation-for="Price"></span>
<button type="submit">Save</button>
</form>
Common Validation Attributes
| Attribute | Purpose |
|---|---|
[Required] |
Value must be provided |
[StringLength] |
Controls string length |
[Range] |
Defines numeric range |
[EmailAddress] |
Validates email format |
[Phone] |
Validates phone format |
[Compare] |
Compares two properties |
[RegularExpression] |
Validates against a pattern |
Client-Side vs Server-Side Validation
| Client-side | Server-side |
|---|---|
| Runs in the browser | Runs on the server |
| Gives immediate feedback | Protects against invalid requests |
| Can be bypassed | Cannot be trusted to the browser |
| Improves user experience | Mandatory for security |
Key Points
- Model binding creates and populates the model first.
- Validation runs after model binding.
- Validation errors are stored in
ModelState. - In an MVC controller, check
ModelState.IsValidmanually. - Return the same View with the submitted model when validation fails.
- Always perform server-side validation.
- Use a ViewModel for UI validation instead of adding every UI rule to database entities.
- Complex business rules should also be enforced in the service or domain layer.
What Is ModelState?
ModelState stores the results of:
- Model binding
- Data conversion
- Model validation
It contains both submitted values and validation errors.
Example
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Create(ProductViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
// Save valid data
return RedirectToAction("Index");
}
Why Must It Be Checked?
- Prevents invalid data from being saved.
- Detects missing required fields.
- Detects validation-attribute failures.
- Detects type-conversion errors.
- Returns validation messages to the View.
- Improves data integrity and application security.
Example Validation Error
public class ProductViewModel
{
[Required]
public string Name { get; set; } = string.Empty;
[Range(1, 100000)]
public decimal Price { get; set; }
}
Invalid form data:
Name = empty
Price = abc
ModelState contains:
Namevalidation error: value is required.Pricebinding error:abccannot be converted todecimal.ModelState.IsValidbecomesfalse.
Add a Custom Error
if (model.Price < 100)
{
ModelState.AddModelError(
nameof(model.Price),
"Price must be at least 100.");
}
Model-level error:
ModelState.AddModelError(
string.Empty,
"Unable to save the product.");
Key Points
ModelState.IsValidistrueonly when there are no binding or validation errors.- Invalid models do not automatically stop normal MVC actions.
- Return the same View with the model when validation fails.
- Validation Tag Helpers display errors stored in
ModelState. - Never save data before checking
ModelState.IsValid. [ApiController]usually returns HTTP400automatically for invalid API models; standard MVC controllers normally require a manual check.