Currently, I am developing a NestJS application where my main task is to create diverse entities such as Users, Products, Orders, etc., and perform specific transformations before storing them in the database. One of my requirements is to include a version number and some IDs to certain types of entities (e.g., User).
To handle these additional properties, I decided to introduce new interfaces (EntityWithVersion, EntityWithIds). My approach aims to ensure type safety while adding flexibility to the entities. This raises the question of whether it's better to use an interface that encompasses the entire object instead.
As I am proficient in NestJS, I will make use of it to demonstrate this concept:
interface CreateEntityDto {
type: EntityType;
data: any; // Generalized to any type
}
enum EntityType {
User,
Product,
Order,
}
interface EntityWithVersion extends CreateEntityDto {
version: number;
}
interface EntityWithIds extends EntityWithVersion {
ids: string[];
}
@Injectable()
export class EntityService {
constructor(
@InjectModel('Entity') private readonly entityModel: Model<Entity>,
) {}
async create(createEntityDto: CreateEntityDto): Promise<Entity> {
if (createEntityDto.type === EntityType.User && createEntityDto.data) {
const entityWithVersion = await this.addEntityVersion(createEntityDto);
const entityWithIds = await this.addEntityIds(entityWithVersion);
return await this.entityModel.create(entityWithIds);
}
return await this.entityModel.create(createEntityDto);
}
async addEntityVersion(
entity: CreateEntityDto,
): Promise<EntityWithVersion> {
return {
...entity,
version: 1,
};
}
async addEntityIds(
entity: EntityWithVersion,
): Promise<EntityWithIds> {
return {
...entity,
ids: ['id1', 'id2'],
};
}
}
During the development of my project, I encountered scenarios where objects underwent multiple transformations, resulting in the addition of new properties at different stages. To maintain type safety and enhance the structure of the entities, I opted for creating distinct interfaces that extend the original data transfer object (DTO) whenever a new property was introduced.