By Manaswini De Software Engineer & Instructor at CodeKerdos
If you’ve spent any time building backend applications – whether in Java, Python, Node.js, or any other language – you’ve probably stumbled across the terms DTO, Entity, and Model. They sound similar, they look similar in code, and half the Stack Overflow threads just make it more confusing.
But here’s the thing: getting this right isn’t just academic. Misusing these patterns leads to bloated code, tight coupling, security leaks (ever accidentally serialized a password hash to the client?), and maintenance nightmares.
In this blog, we’ll break down exactly what each term means, where each lives in your application, and when to use which. Let’s clear up the confusion once and for all.
Before diving into definitions, let’s picture how a typical HTTP request travels through a layered backend. Notice how different types appear at different stages:
A DTO (Data Transfer Object) is a plain object whose sole job is to carry data from one place to another – typically across layer boundaries or over a network. It contains no business logic whatsoever.
// Request DTO - what the client sends public class CreateUserRequestDTO { private String name; private String email; private String password; // raw password from client // getters and setters... } // Response DTO - what we send back public class UserResponseDTO { private Long id; private String name; private String email; // Note: NO password field -- intentionally hidden }
Security tip: DTOs are your API contract. By controlling which fields appear in response DTOs, you prevent accidentally leaking sensitive data like password hashes, internal IDs, or audit fields to the client.
An Entity is a class that maps directly to a database table (or document, in NoSQL). It is the object that your ORM (Object-Relational Mapper) – like Hibernate, JPA, SQLAlchemy, or Sequelize – manages for you. When you save, update, or delete an entity, the ORM translates those operations into SQL.
@Entity @Table(name = "users") public class UserEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false) private String name; @Column(unique = true, nullable = false) private String email; private String passwordHash; // hashed, never expose via DTO @CreationTimestamp private LocalDateTime createdAt; }
Common mistake: Returning Entity objects directly from REST controllers. This exposes your DB schema, internal fields, and can cause infinite recursion with lazy-loaded relationships. Always map to a DTO first.
The term “Model” is the most overloaded of the three – it means different things in different contexts. In Domain-Driven Design (DDD), a Domain Model is a rich object that encapsulates both state and behaviour. In MVC frameworks, a “Model” might refer to a ViewModel – data shaped for a specific view. In some codebases, it’s simply the business object that sits between the DTO and the Entity.
public class User { // Domain Model private Long id; private String name; private Email email; // Value object private PasswordHash hash; public void changeEmail(String newEmail) { if (!EmailValidator.isValid(newEmail)) throw new InvalidEmailException(newEmail); this.email = new Email(newEmail); } public boolean isActive() { return this.status == UserStatus.ACTIVE; } }
|
Aspect |
DTO |
Entity |
Model |
|
Purpose |
Transfer data |
Map to DB |
Business / UI logic |
|
Location |
API / Service layer |
Repository / DB layer |
Domain / Presentation |
|
Contains |
Fields only |
Fields + DB annotations |
Fields + business rules |
|
ORM Managed |
No |
Yes (JPA/Hibernate etc.) |
Usually No |
|
Validation |
Input validation |
DB constraints |
Business rules |
|
Serialized |
Yes (JSON/XML) |
Sometimes |
Sometimes |
|
Frameworks |
Jackson, Gson |
JPA, Hibernate, SQLAlchemy |
Any |
A well-structured backend application separates concerns into layers. Each layer has clear responsibilities, and each of our three types belongs to specific layers:
Let’s trace a real request through a Spring Boot application step by step:
POST /api/users { "name": "John Doe", "email": "john@codekerdos.com", "password": "SecurePass123" }
@PostMapping("/users") public ResponseEntity<UserResponseDTO> createUser( @RequestBody CreateUserRequestDTO request) { UserResponseDTO response = userService.create(request); return ResponseEntity.status(201).body(response); }
public UserResponseDTO create(CreateUserRequestDTO dto) { User user = new User(dto.getName(), dto.getEmail()); user.setPasswordHash(passwordEncoder.encode(dto.getPassword())); UserEntity entity = userMapper.toEntity(user); UserEntity saved = userRepository.save(entity); return userMapper.toResponseDTO(saved); }
{
"id": 42,
"name": "John Doe",
"email": "john@codekerdos.com"
// password hash: NOT here
}
❌ Returning Entity directly from a REST controller. Always map to a DTO – you risk exposing DB internals and lazy-load exceptions.
❌ Returning Entity directly from a REST controller. Always map to a DTO – you risk exposing DB internals and lazy-load exceptions.
❌ Skipping the Domain Model in simple CRUD apps. Fair – but as complexity grows, you’ll regret not having a clean business layer.
✅ Use a Mapper (MapStruct, ModelMapper, or hand-coded) to convert between DTO ↔ Entity ↔ Model. Never scatter conversion logic across your codebase.
Ask yourself these questions when deciding which type to reach for:
A DTO (Data Transfer Object) is used to transfer data between layers or systems without exposing internal business logic or database structures.
A DTO transfers API data, while an Entity maps directly to database tables managed by an ORM like Hibernate or JPA.
Exposing Entities can leak sensitive database fields, create tight coupling, and cause serialization or lazy-loading issues.
A Domain Model represents business concepts and contains business logic independent of frameworks or databases.
Layered architecture separates applications into presentation, business, service, and database layers for better maintainability and scalability.
Use DTOs when transferring data between clients, APIs, services, or application layers.
The DTO, Entity, and Model pattern is one of those architectural decisions that seems pedantic at first but pays enormous dividends as your application scales. To recap:
Keeping these three types distinct might feel like extra boilerplate in a tiny app. But it gives you clean layer boundaries, easier testing, better security, and the freedom to change your DB schema without breaking your API.