Projected Entity
In the CQRS architecture, a projected entity is used to store information that has been projected from one or more events. This allows the system to create materialized views, which are optimized for specific read queries and can significantly improve query performance by denormalizing data structures.
Projected entities are typically used to optimize the read side of a CQRS-based system, where complex joins or transformations would be costly if executed on the fly. By precomputing and storing these views, we can serve queries more efficiently.
Implementation
The projected entity itself defines the interface for the entity and specifies the database schema through TypeORM annotations. This entity is responsible for both defining the shape of the data and persisting it to the database.
Below is an implementation of a projected entity, found in the following file: Task/src/shared/application/readmodels/ActiveTaskOverviewEntity.ts
.
import { Entity, Column, PrimaryColumn, BaseEntity } from "typeorm";
import { Clock } from "@codebricks/codebricks-framework";
export interface ActiveTaskOverviewProperties {
taskId: string;
title: string;
description: string;
assigneeId: string;
}
@Entity('active_task_overview')
export class ActiveTaskOverviewEntity extends BaseEntity {
static readonly defaultValue: ActiveTaskOverviewProperties = {
taskId: '',
title: '',
description: '',
assigneeId: '',
};
@PrimaryColumn({ name: 'task_id', type: 'uuid'})
taskId: string;
@Column({ name: 'title', type: 'text'})
title: string;
@Column({ name: 'description', type: 'text'})
description: string;
@Column({ name: 'assignee_id', type: 'uuid'})
assigneeId: string;
constructor(props?: ActiveTaskOverviewProperties) {
super();
if (props){
this.taskId = props.taskId;
this.title = props.title;
this.description = props.description;
this.assigneeId = props.assigneeId;
} else {
this.taskId = ActiveTaskOverviewEntity.defaultValue.taskId;
this.title = ActiveTaskOverviewEntity.defaultValue.title;
this.description = ActiveTaskOverviewEntity.defaultValue.description;
this.assigneeId = ActiveTaskOverviewEntity.defaultValue.assigneeId;
}
}
}
Code Explanation
- Entity Definition: The
ActiveTaskOverviewEntity
class is decorated with TypeORM annotations, marking it as a database entity tied to theactive_task_overview
table. - Primary Column: The
taskId
field is marked as the primary key, ensuring each task overview is uniquely identifiable. - Columns: The
title
,description
, andassigneeId
fields are annotated as columns, defining their types and how they map to the database schema. - Constructor: The constructor allows for the instantiation of the entity with specific properties. If no properties are provided, it defaults to the
defaultValue
.
Key Concepts
- Interface Definition: The
ActiveTaskOverviewProperties
interface defines the structure of the data that theActiveTaskOverviewEntity
will hold. It acts as a contract for the entity’s shape, ensuring consistency. - TypeORM Integration: TypeORM is used to define the schema and interact with the database. The
@Entity
,@PrimaryColumn
, and@Column
decorators link the entity's properties to the database structure. - Materialized View: The projected entity acts as a materialized view, providing a read-optimized representation of the data. This is crucial in CQRS architectures where the read and write models are separated.