mirror of
https://github.com/samiyev/puaros.git
synced 2025-12-27 23:06:54 +05:00
140 lines
3.6 KiB
TypeScript
140 lines
3.6 KiB
TypeScript
/**
|
|
* GOOD EXAMPLE: Rich Domain Model with Business Logic
|
|
*
|
|
* This Customer class encapsulates business rules and state transitions.
|
|
* No public setters - all changes go through business methods.
|
|
*
|
|
* This follows Domain-Driven Design and encapsulation principles.
|
|
*/
|
|
|
|
interface Address {
|
|
street: string
|
|
city: string
|
|
country: string
|
|
postalCode: string
|
|
}
|
|
|
|
interface DomainEvent {
|
|
type: string
|
|
data: any
|
|
}
|
|
|
|
class Customer {
|
|
private readonly id: string
|
|
private email: string
|
|
private isActive: boolean
|
|
private loyaltyPoints: number
|
|
private address: Address | null
|
|
private readonly events: DomainEvent[] = []
|
|
|
|
constructor(id: string, email: string) {
|
|
this.id = id
|
|
this.email = email
|
|
this.isActive = true
|
|
this.loyaltyPoints = 0
|
|
this.address = null
|
|
}
|
|
|
|
public activate(): void {
|
|
if (this.isActive) {
|
|
throw new Error("Customer is already active")
|
|
}
|
|
this.isActive = true
|
|
this.events.push({
|
|
type: "CustomerActivated",
|
|
data: { customerId: this.id },
|
|
})
|
|
}
|
|
|
|
public deactivate(reason: string): void {
|
|
if (!this.isActive) {
|
|
throw new Error("Customer is already inactive")
|
|
}
|
|
this.isActive = false
|
|
this.events.push({
|
|
type: "CustomerDeactivated",
|
|
data: { customerId: this.id, reason },
|
|
})
|
|
}
|
|
|
|
public changeEmail(newEmail: string): void {
|
|
if (!this.isValidEmail(newEmail)) {
|
|
throw new Error("Invalid email format")
|
|
}
|
|
if (this.email === newEmail) {
|
|
return
|
|
}
|
|
const oldEmail = this.email
|
|
this.email = newEmail
|
|
this.events.push({
|
|
type: "EmailChanged",
|
|
data: { customerId: this.id, oldEmail, newEmail },
|
|
})
|
|
}
|
|
|
|
public updateAddress(address: Address): void {
|
|
if (!this.isValidAddress(address)) {
|
|
throw new Error("Invalid address")
|
|
}
|
|
this.address = address
|
|
this.events.push({
|
|
type: "AddressUpdated",
|
|
data: { customerId: this.id },
|
|
})
|
|
}
|
|
|
|
public addLoyaltyPoints(points: number): void {
|
|
if (points <= 0) {
|
|
throw new Error("Points must be positive")
|
|
}
|
|
if (!this.isActive) {
|
|
throw new Error("Cannot add points to inactive customer")
|
|
}
|
|
this.loyaltyPoints += points
|
|
this.events.push({
|
|
type: "LoyaltyPointsAdded",
|
|
data: { customerId: this.id, points },
|
|
})
|
|
}
|
|
|
|
public redeemLoyaltyPoints(points: number): void {
|
|
if (points <= 0) {
|
|
throw new Error("Points must be positive")
|
|
}
|
|
if (this.loyaltyPoints < points) {
|
|
throw new Error("Insufficient loyalty points")
|
|
}
|
|
this.loyaltyPoints -= points
|
|
this.events.push({
|
|
type: "LoyaltyPointsRedeemed",
|
|
data: { customerId: this.id, points },
|
|
})
|
|
}
|
|
|
|
public getEmail(): string {
|
|
return this.email
|
|
}
|
|
|
|
public getLoyaltyPoints(): number {
|
|
return this.loyaltyPoints
|
|
}
|
|
|
|
public getAddress(): Address | null {
|
|
return this.address ? { ...this.address } : null
|
|
}
|
|
|
|
public getEvents(): DomainEvent[] {
|
|
return [...this.events]
|
|
}
|
|
|
|
private isValidEmail(email: string): boolean {
|
|
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)
|
|
}
|
|
|
|
private isValidAddress(address: Address): boolean {
|
|
return !!address.street && !!address.city && !!address.country && !!address.postalCode
|
|
}
|
|
}
|
|
|
|
export { Customer }
|