Strategic architecture, tactical design, and testable code principles (SOLID, Clean Architecture, Design Patterns, Testable Design)
71
89%
Does it follow best practices?
Impact
—
No eval scenarios have been run
Passed
No known issues
Mappers translate between domain entities and external representations (database rows, API responses, DTOs). Each layer has its own model; mappers bridge them.
Incorrect (domain entity used everywhere):
// Domain entity directly serialized to JSON and stored in DB
class User {
id: string
email: string
passwordHash: string // Exposed in API response!
createdAt: Date
lastLogin: Date
preferences: UserPreferences
roles: Role[]
toJSON() {
return { ...this } // Leaks everything
}
}
// Controller returns entity directly
app.get('/users/:id', (req, res) => {
const user = userRepo.findById(req.params.id)
res.json(user) // passwordHash in response!
})
// ORM couples domain to database schema
@Entity()
class User {
@PrimaryColumn() id: string
@Column() email: string
// Domain entity is now a database entity
}Correct (dedicated models per layer with mappers):
// domain/User.ts - Pure domain entity
class User {
constructor(
readonly id: UserId,
private email: Email,
private passwordHash: PasswordHash,
private roles: Set<Role>
) {}
hasRole(role: Role): boolean {
return this.roles.has(role)
}
}
// infrastructure/persistence/UserEntity.ts - Database model
interface UserRow {
id: string
email: string
password_hash: string
roles: string // JSON array in DB
created_at: string
updated_at: string
}
// infrastructure/persistence/UserMapper.ts
class UserMapper {
toDomain(row: UserRow): User {
return new User(
new UserId(row.id),
Email.create(row.email),
new PasswordHash(row.password_hash),
new Set(JSON.parse(row.roles))
)
}
toPersistence(user: User): UserRow {
return {
id: user.id.value,
email: user.email.value,
password_hash: user.passwordHash.value,
roles: JSON.stringify([...user.roles]),
updated_at: new Date().toISOString()
}
}
}
// interface/dto/UserResponse.ts - API response model
interface UserResponse {
id: string
email: string
roles: string[]
// No passwordHash!
}
// interface/mappers/UserResponseMapper.ts
class UserResponseMapper {
toResponse(user: User): UserResponse {
return {
id: user.id.value,
email: user.email.value,
roles: [...user.roles]
}
}
}Benefits:
Reference: Data Mapper Pattern
.tessl-plugin
clean-architecture
references
design-patterns
references
solid-principles
testable-design