feat(core): add domain layer with clean architecture

Add domain layer components:
- BaseEntity with ID, timestamps and equality checks
- ValueObject for immutable value objects
- IRepository interface for persistence
- DomainEvent system for domain events
This commit is contained in:
imfozilbek
2025-11-23 21:43:34 +05:00
parent 6fe90a708b
commit 2bdfc5382f
5 changed files with 110 additions and 0 deletions

View File

@@ -0,0 +1,44 @@
import { v4 as uuidv4 } from 'uuid';
/**
* Base entity class with ID and timestamps
*/
export abstract class BaseEntity {
protected readonly _id: string;
protected readonly _createdAt: Date;
protected _updatedAt: Date;
constructor(id?: string) {
this._id = id ?? uuidv4();
this._createdAt = new Date();
this._updatedAt = new Date();
}
public get id(): string {
return this._id;
}
public get createdAt(): Date {
return this._createdAt;
}
public get updatedAt(): Date {
return this._updatedAt;
}
protected touch(): void {
this._updatedAt = new Date();
}
public equals(entity: BaseEntity): boolean {
if (entity === null || entity === undefined) {
return false;
}
if (this === entity) {
return true;
}
return this._id === entity._id;
}
}

View File

@@ -0,0 +1,25 @@
import { v4 as uuidv4 } from 'uuid';
/**
* Base interface for all domain events
*/
export interface IDomainEvent {
readonly eventId: string;
readonly occurredOn: Date;
readonly eventType: string;
}
/**
* Base class for domain events
*/
export abstract class DomainEvent implements IDomainEvent {
public readonly eventId: string;
public readonly occurredOn: Date;
public readonly eventType: string;
constructor(eventType: string) {
this.eventId = uuidv4();
this.occurredOn = new Date();
this.eventType = eventType;
}
}

View File

@@ -0,0 +1,4 @@
export * from './entities/BaseEntity';
export * from './value-objects/ValueObject';
export * from './repositories/IRepository';
export * from './events/DomainEvent';

View File

@@ -0,0 +1,14 @@
import { BaseEntity } from '../entities/BaseEntity';
/**
* Generic repository interface
* Defines standard CRUD operations for entities
*/
export interface IRepository<T extends BaseEntity> {
findById(id: string): Promise<T | null>;
findAll(): Promise<T[]>;
save(entity: T): Promise<T>;
update(entity: T): Promise<T>;
delete(id: string): Promise<boolean>;
exists(id: string): Promise<boolean>;
}

View File

@@ -0,0 +1,23 @@
/**
* Base class for Value Objects
* Value objects are immutable and compared by value, not identity
*/
export abstract class ValueObject<T> {
protected readonly props: T;
constructor(props: T) {
this.props = Object.freeze(props);
}
public equals(vo?: ValueObject<T>): boolean {
if (vo === null || vo === undefined) {
return false;
}
if (vo.props === undefined) {
return false;
}
return JSON.stringify(this.props) === JSON.stringify(vo.props);
}
}