commit
7b999a670c
243 files changed
+24818
No files matched your search
@@ -0,0 +1,180 @@
|
||||
import { IssueTitle } from '../value-objects/issue-title.vo.js';
|
||||
import { IssueStatus } from '../value-objects/issue-status.vo.js';
|
||||
import { IssuePriority } from '../value-objects/issue-priority.vo.js';
|
||||
import { IssueCreatedEvent } from '../events/issue-created.event.js';
|
||||
import { IssueUpdatedEvent } from '../events/issue-updated.event.js';
|
||||
|
||||
interface IssueProps {
|
||||
id: number | null;
|
||||
key: string;
|
||||
title: IssueTitle;
|
||||
content: string | null;
|
||||
status: IssueStatus;
|
||||
priority: IssuePriority;
|
||||
assignee: string | null;
|
||||
reporter: string;
|
||||
projectId: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
/**
|
||||
* Доменная сущность. Никогда не импортирует ничего из infrastructure/
|
||||
* presentation — только чистая бизнес-логика и инварианты.
|
||||
*
|
||||
* `id` — Int, генерируется базой данных (SERIAL), поэтому у новой задачи
|
||||
* он равен null до сохранения в репозитории. `key` (например, "ISSUE-1")
|
||||
* присваивается на уровне приложения при создании и не зависит от id.
|
||||
*/
|
||||
export class Issue {
|
||||
private domainEvents: unknown[] = [];
|
||||
|
||||
private constructor(private readonly props: IssueProps) {}
|
||||
|
||||
/** Фабричный метод для создания НОВОЙ задачи (порождает доменное событие) */
|
||||
static create(params: {
|
||||
key: string;
|
||||
title: string;
|
||||
content?: string | null;
|
||||
priority?: number;
|
||||
assignee?: string | null;
|
||||
reporter: string;
|
||||
projectId: string;
|
||||
}): Issue {
|
||||
const issue = new Issue({
|
||||
id: null,
|
||||
key: params.key,
|
||||
title: IssueTitle.create(params.title),
|
||||
content: params.content ?? null,
|
||||
status: IssueStatus.open(),
|
||||
priority: params.priority !== undefined ? IssuePriority.create(params.priority) : IssuePriority.default(),
|
||||
assignee: params.assignee ?? null,
|
||||
reporter: params.reporter,
|
||||
projectId: params.projectId,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
});
|
||||
|
||||
issue.addDomainEvent(new IssueCreatedEvent(issue.key));
|
||||
|
||||
return issue;
|
||||
}
|
||||
|
||||
/** Восстановление сущности из персистентного слоя (без событий) */
|
||||
static reconstitute(props: {
|
||||
id: number;
|
||||
key: string;
|
||||
title: string;
|
||||
content: string | null;
|
||||
status: string;
|
||||
priority: number;
|
||||
assignee: string | null;
|
||||
reporter: string;
|
||||
projectId: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}): Issue {
|
||||
return new Issue({
|
||||
id: props.id,
|
||||
key: props.key,
|
||||
title: IssueTitle.create(props.title),
|
||||
content: props.content,
|
||||
status: IssueStatus.create(props.status),
|
||||
priority: IssuePriority.create(props.priority),
|
||||
assignee: props.assignee,
|
||||
reporter: props.reporter,
|
||||
projectId: props.projectId,
|
||||
createdAt: props.createdAt,
|
||||
updatedAt: props.updatedAt,
|
||||
});
|
||||
}
|
||||
|
||||
get id(): number | null {
|
||||
return this.props.id;
|
||||
}
|
||||
|
||||
get key(): string {
|
||||
return this.props.key;
|
||||
}
|
||||
|
||||
get title(): string {
|
||||
return this.props.title.value;
|
||||
}
|
||||
|
||||
get content(): string | null {
|
||||
return this.props.content;
|
||||
}
|
||||
|
||||
get status(): string {
|
||||
return this.props.status.value;
|
||||
}
|
||||
|
||||
get priority(): number {
|
||||
return this.props.priority.value;
|
||||
}
|
||||
|
||||
get assignee(): string | null {
|
||||
return this.props.assignee;
|
||||
}
|
||||
|
||||
get reporter(): string {
|
||||
return this.props.reporter;
|
||||
}
|
||||
|
||||
get projectId(): string {
|
||||
return this.props.projectId;
|
||||
}
|
||||
|
||||
get createdAt(): Date {
|
||||
return this.props.createdAt;
|
||||
}
|
||||
|
||||
get updatedAt(): Date {
|
||||
return this.props.updatedAt;
|
||||
}
|
||||
|
||||
updateTitle(title: string): void {
|
||||
this.props.title = IssueTitle.create(title);
|
||||
this.touch();
|
||||
}
|
||||
|
||||
updateContent(content: string | null): void {
|
||||
this.props.content = content;
|
||||
this.touch();
|
||||
}
|
||||
|
||||
changeStatus(status: string): void {
|
||||
this.props.status = IssueStatus.create(status);
|
||||
this.touch();
|
||||
}
|
||||
|
||||
changePriority(priority: number): void {
|
||||
this.props.priority = IssuePriority.create(priority);
|
||||
this.touch();
|
||||
}
|
||||
|
||||
assignTo(userId: string): void {
|
||||
this.props.assignee = userId;
|
||||
this.touch();
|
||||
}
|
||||
|
||||
unassign(): void {
|
||||
this.props.assignee = null;
|
||||
this.touch();
|
||||
}
|
||||
|
||||
private touch(): void {
|
||||
this.props.updatedAt = new Date();
|
||||
this.addDomainEvent(new IssueUpdatedEvent(this.key));
|
||||
}
|
||||
|
||||
private addDomainEvent(event: unknown): void {
|
||||
this.domainEvents.push(event);
|
||||
}
|
||||
|
||||
pullDomainEvents(): unknown[] {
|
||||
const events = [...this.domainEvents];
|
||||
this.domainEvents = [];
|
||||
return events;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user