Skip to main content

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 the active_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, and assigneeId 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 the ActiveTaskOverviewEntity 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.