13.1 架构设计原则
13.1.1 SOLID原则在TypeScript中的应用
// src/principles/solid.ts - SOLID原则示例
// 1. 单一职责原则 (Single Responsibility Principle)
interface UserRepository {
findById(id: string): Promise<User | null>;
save(user: User): Promise<void>;
delete(id: string): Promise<void>;
}
interface UserValidator {
validate(user: User): ValidationResult;
}
interface EmailService {
sendWelcomeEmail(user: User): Promise<void>;
}
// 错误示例:违反单一职责原则
class BadUserService {
async createUser(userData: CreateUserRequest): Promise<User> {
// 验证用户数据
if (!userData.email || !userData.password) {
throw new Error('Invalid user data');
}
// 保存用户
const user = new User(userData);
await this.saveToDatabase(user);
// 发送邮件
await this.sendEmail(user.email, 'Welcome!');
// 记录日志
console.log(`User created: ${user.id}`);
return user;
}
private async saveToDatabase(user: User): Promise<void> { /* ... */ }
private async sendEmail(email: string, subject: string): Promise<void> { /* ... */ }
}
// 正确示例:遵循单一职责原则
class UserService {
constructor(
private userRepository: UserRepository,
private userValidator: UserValidator,
private emailService: EmailService,
private logger: Logger
) {}
async createUser(userData: CreateUserRequest): Promise<User> {
// 验证
const validationResult = this.userValidator.validate(userData);
if (!validationResult.isValid) {
throw new ValidationError(validationResult.errors);
}
// 创建用户
const user = new User(userData);
// 保存
await this.userRepository.save(user);
// 发送欢迎邮件
await this.emailService.sendWelcomeEmail(user);
// 记录日志
this.logger.info(`User created: ${user.id}`);
return user;
}
}
// 2. 开闭原则 (Open/Closed Principle)
abstract class PaymentProcessor {
abstract processPayment(amount: number, paymentMethod: PaymentMethod): Promise<PaymentResult>;
protected validateAmount(amount: number): void {
if (amount <= 0) {
throw new Error('Amount must be positive');
}
}
}
class CreditCardProcessor extends PaymentProcessor {
async processPayment(amount: number, paymentMethod: CreditCardPayment): Promise<PaymentResult> {
this.validateAmount(amount);
// 信用卡特定的处理逻辑
const result = await this.chargeCreditCard(amount, paymentMethod.cardNumber);
return result;
}
private async chargeCreditCard(amount: number, cardNumber: string): Promise<PaymentResult> {
// 信用卡支付逻辑
return { success: true, transactionId: 'cc_' + Date.now() };
}
}
class PayPalProcessor extends PaymentProcessor {
async processPayment(amount: number, paymentMethod: PayPalPayment): Promise<PaymentResult> {
this.validateAmount(amount);
// PayPal特定的处理逻辑
const result = await this.chargePayPal(amount, paymentMethod.email);
return result;
}
private async chargePayPal(amount: number, email: string): Promise<PaymentResult> {
// PayPal支付逻辑
return { success: true, transactionId: 'pp_' + Date.now() };
}
}
// 3. 里氏替换原则 (Liskov Substitution Principle)
interface Bird {
move(): void;
}
interface FlyingBird extends Bird {
fly(): void;
}
interface SwimmingBird extends Bird {
swim(): void;
}
class Eagle implements FlyingBird {
move(): void {
this.fly();
}
fly(): void {
console.log('Eagle is flying');
}
}
class Penguin implements SwimmingBird {
move(): void {
this.swim();
}
swim(): void {
console.log('Penguin is swimming');
}
}
// 4. 接口隔离原则 (Interface Segregation Principle)
// 错误示例:臃肿的接口
interface BadWorker {
work(): void;
eat(): void;
sleep(): void;
code(): void;
design(): void;
test(): void;
}
// 正确示例:细分的接口
interface Worker {
work(): void;
}
interface Human {
eat(): void;
sleep(): void;
}
interface Developer {
code(): void;
}
interface Designer {
design(): void;
}
interface Tester {
test(): void;
}
class SoftwareDeveloper implements Worker, Human, Developer {
work(): void {
this.code();
}
eat(): void {
console.log('Developer is eating');
}
sleep(): void {
console.log('Developer is sleeping');
}
code(): void {
console.log('Developer is coding');
}
}
// 5. 依赖倒置原则 (Dependency Inversion Principle)
interface NotificationService {
send(message: string, recipient: string): Promise<void>;
}
class EmailNotificationService implements NotificationService {
async send(message: string, recipient: string): Promise<void> {
console.log(`Sending email to ${recipient}: ${message}`);
}
}
class SMSNotificationService implements NotificationService {
async send(message: string, recipient: string): Promise<void> {
console.log(`Sending SMS to ${recipient}: ${message}`);
}
}
// 高层模块依赖抽象,而不是具体实现
class OrderService {
constructor(private notificationService: NotificationService) {}
async processOrder(order: Order): Promise<void> {
// 处理订单逻辑
await this.saveOrder(order);
// 发送通知
await this.notificationService.send(
`Order ${order.id} has been processed`,
order.customerEmail
);
}
private async saveOrder(order: Order): Promise<void> {
// 保存订单逻辑
}
}
13.1.2 领域驱动设计 (DDD)
// src/domain/user/entities/User.ts - 用户实体
export class User {
private constructor(
private readonly _id: UserId,
private _email: Email,
private _profile: UserProfile,
private _status: UserStatus,
private readonly _createdAt: Date,
private _updatedAt: Date
) {}
static create(email: string, profile: UserProfileData): User {
const userId = UserId.generate();
const userEmail = Email.create(email);
const userProfile = UserProfile.create(profile);
return new User(
userId,
userEmail,
userProfile,
UserStatus.ACTIVE,
new Date(),
new Date()
);
}
static fromPersistence(data: UserPersistenceData): User {
return new User(
UserId.fromString(data.id),
Email.create(data.email),
UserProfile.fromPersistence(data.profile),
UserStatus.fromString(data.status),
new Date(data.createdAt),
new Date(data.updatedAt)
);
}
// Getters
get id(): UserId { return this._id; }
get email(): Email { return this._email; }
get profile(): UserProfile { return this._profile; }
get status(): UserStatus { return this._status; }
get createdAt(): Date { return this._createdAt; }
get updatedAt(): Date { return this._updatedAt; }
// 业务方法
updateEmail(newEmail: string): void {
const email = Email.create(newEmail);
this._email = email;
this._updatedAt = new Date();
// 发布领域事件
DomainEvents.raise(new UserEmailUpdatedEvent(this._id, email));
}
updateProfile(profileData: UserProfileData): void {
this._profile = UserProfile.create(profileData);
this._updatedAt = new Date();
DomainEvents.raise(new UserProfileUpdatedEvent(this._id, this._profile));
}
deactivate(): void {
if (this._status === UserStatus.INACTIVE) {
throw new DomainError('User is already inactive');
}
this._status = UserStatus.INACTIVE;
this._updatedAt = new Date();
DomainEvents.raise(new UserDeactivatedEvent(this._id));
}
activate(): void {
if (this._status === UserStatus.ACTIVE) {
throw new DomainError('User is already active');
}
this._status = UserStatus.ACTIVE;
this._updatedAt = new Date();
DomainEvents.raise(new UserActivatedEvent(this._id));
}
toPersistence(): UserPersistenceData {
return {
id: this._id.value,
email: this._email.value,
profile: this._profile.toPersistence(),
status: this._status.value,
createdAt: this._createdAt.toISOString(),
updatedAt: this._updatedAt.toISOString()
};
}
}
// src/domain/user/value-objects/UserId.ts - 用户ID值对象
export class UserId {
private constructor(private readonly _value: string) {
this.validate(_value);
}
static generate(): UserId {
return new UserId(crypto.randomUUID());
}
static fromString(value: string): UserId {
return new UserId(value);
}
get value(): string {
return this._value;
}
equals(other: UserId): boolean {
return this._value === other._value;
}
private validate(value: string): void {
if (!value || typeof value !== 'string') {
throw new DomainError('UserId must be a non-empty string');
}
// UUID格式验证
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
if (!uuidRegex.test(value)) {
throw new DomainError('UserId must be a valid UUID');
}
}
}
// src/domain/user/value-objects/Email.ts - 邮箱值对象
export class Email {
private constructor(private readonly _value: string) {
this.validate(_value);
}
static create(value: string): Email {
return new Email(value);
}
get value(): string {
return this._value;
}
get domain(): string {
return this._value.split('@')[1];
}
get localPart(): string {
return this._value.split('@')[0];
}
equals(other: Email): boolean {
return this._value.toLowerCase() === other._value.toLowerCase();
}
private validate(value: string): void {
if (!value || typeof value !== 'string') {
throw new DomainError('Email must be a non-empty string');
}
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(value)) {
throw new DomainError('Invalid email format');
}
if (value.length > 254) {
throw new DomainError('Email is too long');
}
}
}
// src/domain/user/repositories/UserRepository.ts - 用户仓储接口
export interface UserRepository {
findById(id: UserId): Promise<User | null>;
findByEmail(email: Email): Promise<User | null>;
save(user: User): Promise<void>;
delete(id: UserId): Promise<void>;
findAll(criteria?: UserSearchCriteria): Promise<User[]>;
count(criteria?: UserSearchCriteria): Promise<number>;
}
export interface UserSearchCriteria {
status?: UserStatus;
emailDomain?: string;
createdAfter?: Date;
createdBefore?: Date;
limit?: number;
offset?: number;
}
// src/domain/user/services/UserDomainService.ts - 领域服务
export class UserDomainService {
constructor(private userRepository: UserRepository) {}
async isEmailUnique(email: Email, excludeUserId?: UserId): Promise<boolean> {
const existingUser = await this.userRepository.findByEmail(email);
if (!existingUser) {
return true;
}
if (excludeUserId && existingUser.id.equals(excludeUserId)) {
return true;
}
return false;
}
async canUserBeDeleted(userId: UserId): Promise<boolean> {
// 检查用户是否有未完成的订单、活跃的会话等
// 这里可能需要调用其他聚合根的仓储
return true; // 简化实现
}
}
// src/domain/user/events/UserEvents.ts - 领域事件
export abstract class UserDomainEvent {
constructor(
public readonly userId: UserId,
public readonly occurredAt: Date = new Date()
) {}
}
export class UserCreatedEvent extends UserDomainEvent {
constructor(
userId: UserId,
public readonly email: Email,
public readonly profile: UserProfile
) {
super(userId);
}
}
export class UserEmailUpdatedEvent extends UserDomainEvent {
constructor(
userId: UserId,
public readonly newEmail: Email
) {
super(userId);
}
}
export class UserDeactivatedEvent extends UserDomainEvent {
constructor(userId: UserId) {
super(userId);
}
}
13.2 微服务架构
13.2.1 服务拆分策略
// src/services/user-service/types/index.ts - 用户服务类型定义
export interface UserServiceConfig {
database: {
host: string;
port: number;
database: string;
username: string;
password: string;
};
redis: {
host: string;
port: number;
password?: string;
};
messaging: {
brokerUrl: string;
exchange: string;
};
auth: {
jwtSecret: string;
jwtExpiresIn: string;
};
}
export interface UserServiceAPI {
// 用户管理
createUser(request: CreateUserRequest): Promise<CreateUserResponse>;
getUserById(id: string): Promise<GetUserResponse>;
updateUser(id: string, request: UpdateUserRequest): Promise<UpdateUserResponse>;
deleteUser(id: string): Promise<void>;
// 认证相关
authenticate(request: AuthenticateRequest): Promise<AuthenticateResponse>;
refreshToken(request: RefreshTokenRequest): Promise<RefreshTokenResponse>;
// 查询
searchUsers(request: SearchUsersRequest): Promise<SearchUsersResponse>;
getUsersByIds(ids: string[]): Promise<GetUsersResponse>;
}
// src/services/user-service/application/UserApplicationService.ts - 应用服务
export class UserApplicationService implements UserServiceAPI {
constructor(
private userRepository: UserRepository,
private userDomainService: UserDomainService,
private passwordService: PasswordService,
private tokenService: TokenService,
private eventBus: EventBus,
private logger: Logger
) {}
async createUser(request: CreateUserRequest): Promise<CreateUserResponse> {
try {
// 验证请求
const validationResult = await this.validateCreateUserRequest(request);
if (!validationResult.isValid) {
throw new ValidationError(validationResult.errors);
}
// 检查邮箱唯一性
const email = Email.create(request.email);
const isEmailUnique = await this.userDomainService.isEmailUnique(email);
if (!isEmailUnique) {
throw new BusinessError('Email already exists');
}
// 创建用户
const hashedPassword = await this.passwordService.hash(request.password);
const user = User.create(request.email, {
firstName: request.firstName,
lastName: request.lastName,
hashedPassword
});
// 保存用户
await this.userRepository.save(user);
// 发布事件
await this.eventBus.publish(new UserCreatedEvent(
user.id,
user.email,
user.profile
));
this.logger.info(`User created: ${user.id.value}`);
return {
id: user.id.value,
email: user.email.value,
profile: user.profile.toResponse(),
createdAt: user.createdAt.toISOString()
};
} catch (error) {
this.logger.error('Failed to create user', { error, request });
throw error;
}
}
async authenticate(request: AuthenticateRequest): Promise<AuthenticateResponse> {
try {
// 查找用户
const email = Email.create(request.email);
const user = await this.userRepository.findByEmail(email);
if (!user) {
throw new AuthenticationError('Invalid credentials');
}
// 验证密码
const isPasswordValid = await this.passwordService.verify(
request.password,
user.profile.hashedPassword
);
if (!isPasswordValid) {
throw new AuthenticationError('Invalid credentials');
}
// 检查用户状态
if (user.status !== UserStatus.ACTIVE) {
throw new AuthenticationError('User account is not active');
}
// 生成令牌
const accessToken = await this.tokenService.generateAccessToken({
userId: user.id.value,
email: user.email.value
});
const refreshToken = await this.tokenService.generateRefreshToken({
userId: user.id.value
});
this.logger.info(`User authenticated: ${user.id.value}`);
return {
accessToken,
refreshToken,
user: {
id: user.id.value,
email: user.email.value,
profile: user.profile.toResponse()
}
};
} catch (error) {
this.logger.error('Authentication failed', { error, email: request.email });
throw error;
}
}
private async validateCreateUserRequest(request: CreateUserRequest): Promise<ValidationResult> {
const errors: string[] = [];
if (!request.email) {
errors.push('Email is required');
}
if (!request.password || request.password.length < 8) {
errors.push('Password must be at least 8 characters long');
}
if (!request.firstName) {
errors.push('First name is required');
}
if (!request.lastName) {
errors.push('Last name is required');
}
return {
isValid: errors.length === 0,
errors
};
}
}
13.2.2 服务间通信
// src/infrastructure/messaging/EventBus.ts - 事件总线
export interface EventBus {
publish<T extends DomainEvent>(event: T): Promise<void>;
subscribe<T extends DomainEvent>(
eventType: string,
handler: EventHandler<T>
): Promise<void>;
}
export interface EventHandler<T extends DomainEvent> {
handle(event: T): Promise<void>;
}
export class RabbitMQEventBus implements EventBus {
private connection: Connection | null = null;
private channel: Channel | null = null;
constructor(
private config: RabbitMQConfig,
private logger: Logger
) {}
async connect(): Promise<void> {
try {
this.connection = await amqp.connect(this.config.url);
this.channel = await this.connection.createChannel();
// 声明交换机
await this.channel.assertExchange(
this.config.exchange,
'topic',
{ durable: true }
);
this.logger.info('Connected to RabbitMQ');
} catch (error) {
this.logger.error('Failed to connect to RabbitMQ', { error });
throw error;
}
}
async publish<T extends DomainEvent>(event: T): Promise<void> {
if (!this.channel) {
throw new Error('EventBus not connected');
}
try {
const routingKey = this.getRoutingKey(event);
const message = JSON.stringify({
eventId: crypto.randomUUID(),
eventType: event.constructor.name,
occurredAt: event.occurredAt.toISOString(),
data: event
});
await this.channel.publish(
this.config.exchange,
routingKey,
Buffer.from(message),
{
persistent: true,
messageId: crypto.randomUUID(),
timestamp: Date.now()
}
);
this.logger.debug('Event published', { eventType: event.constructor.name, routingKey });
} catch (error) {
this.logger.error('Failed to publish event', { error, event });
throw error;
}
}
async subscribe<T extends DomainEvent>(
eventType: string,
handler: EventHandler<T>
): Promise<void> {
if (!this.channel) {
throw new Error('EventBus not connected');
}
try {
const queueName = `${this.config.serviceName}.${eventType}`;
const routingKey = this.getRoutingKeyPattern(eventType);
// 声明队列
await this.channel.assertQueue(queueName, {
durable: true,
arguments: {
'x-dead-letter-exchange': `${this.config.exchange}.dlx`
}
});
// 绑定队列到交换机
await this.channel.bindQueue(queueName, this.config.exchange, routingKey);
// 设置消费者
await this.channel.consume(queueName, async (msg) => {
if (!msg) return;
try {
const eventData = JSON.parse(msg.content.toString());
await handler.handle(eventData.data);
this.channel!.ack(msg);
this.logger.debug('Event handled successfully', { eventType, queueName });
} catch (error) {
this.logger.error('Failed to handle event', { error, eventType, queueName });
// 重试逻辑
const retryCount = (msg.properties.headers?.['x-retry-count'] || 0) + 1;
if (retryCount <= this.config.maxRetries) {
// 延迟重试
setTimeout(() => {
this.channel!.nack(msg, false, true);
}, this.config.retryDelay * retryCount);
} else {
// 发送到死信队列
this.channel!.nack(msg, false, false);
}
}
});
this.logger.info('Subscribed to event', { eventType, queueName });
} catch (error) {
this.logger.error('Failed to subscribe to event', { error, eventType });
throw error;
}
}
private getRoutingKey(event: DomainEvent): string {
const eventType = event.constructor.name;
return `${this.config.serviceName}.${eventType}`;
}
private getRoutingKeyPattern(eventType: string): string {
return `*.${eventType}`;
}
async disconnect(): Promise<void> {
try {
await this.channel?.close();
await this.connection?.close();
this.logger.info('Disconnected from RabbitMQ');
} catch (error) {
this.logger.error('Failed to disconnect from RabbitMQ', { error });
}
}
}
// src/infrastructure/http/ServiceClient.ts - HTTP服务客户端
export class ServiceClient {
private httpClient: AxiosInstance;
constructor(
private config: ServiceClientConfig,
private logger: Logger
) {
this.httpClient = axios.create({
baseURL: config.baseUrl,
timeout: config.timeout || 30000,
headers: {
'Content-Type': 'application/json',
'User-Agent': `${config.serviceName}/1.0.0`
}
});
this.setupInterceptors();
}
private setupInterceptors(): void {
// 请求拦截器
this.httpClient.interceptors.request.use(
(config) => {
// 添加请求ID
config.headers['X-Request-ID'] = crypto.randomUUID();
// 添加认证令牌
if (this.config.authToken) {
config.headers['Authorization'] = `Bearer ${this.config.authToken}`;
}
this.logger.debug('HTTP request', {
method: config.method,
url: config.url,
requestId: config.headers['X-Request-ID']
});
return config;
},
(error) => {
this.logger.error('HTTP request error', { error });
return Promise.reject(error);
}
);
// 响应拦截器
this.httpClient.interceptors.response.use(
(response) => {
this.logger.debug('HTTP response', {
status: response.status,
requestId: response.config.headers['X-Request-ID']
});
return response;
},
(error) => {
const requestId = error.config?.headers['X-Request-ID'];
if (error.response) {
// 服务器响应错误
this.logger.error('HTTP response error', {
status: error.response.status,
data: error.response.data,
requestId
});
} else if (error.request) {
// 网络错误
this.logger.error('HTTP network error', {
message: error.message,
requestId
});
} else {
// 其他错误
this.logger.error('HTTP error', {
message: error.message,
requestId
});
}
return Promise.reject(this.transformError(error));
}
);
}
async get<T>(path: string, params?: Record<string, any>): Promise<T> {
const response = await this.httpClient.get(path, { params });
return response.data;
}
async post<T>(path: string, data?: any): Promise<T> {
const response = await this.httpClient.post(path, data);
return response.data;
}
async put<T>(path: string, data?: any): Promise<T> {
const response = await this.httpClient.put(path, data);
return response.data;
}
async delete<T>(path: string): Promise<T> {
const response = await this.httpClient.delete(path);
return response.data;
}
private transformError(error: AxiosError): ServiceError {
if (error.response) {
const status = error.response.status;
const data = error.response.data as any;
switch (status) {
case 400:
return new ValidationError(data.message || 'Bad Request', data.errors);
case 401:
return new AuthenticationError(data.message || 'Unauthorized');
case 403:
return new AuthorizationError(data.message || 'Forbidden');
case 404:
return new NotFoundError(data.message || 'Not Found');
case 409:
return new ConflictError(data.message || 'Conflict');
case 500:
return new InternalServerError(data.message || 'Internal Server Error');
default:
return new ServiceError(data.message || 'Unknown Error', status);
}
} else if (error.request) {
return new NetworkError('Network Error');
} else {
return new ServiceError(error.message || 'Unknown Error');
}
}
}
13.3 数据访问层设计
13.3.1 Repository模式实现
// src/infrastructure/persistence/UserRepositoryImpl.ts - 用户仓储实现
export class UserRepositoryImpl implements UserRepository {
constructor(
private db: Database,
private logger: Logger,
private cache: CacheService
) {}
async findById(id: UserId): Promise<User | null> {
try {
// 先从缓存查找
const cacheKey = `user:${id.value}`;
const cachedData = await this.cache.get<UserPersistenceData>(cacheKey);
if (cachedData) {
this.logger.debug('User found in cache', { userId: id.value });
return User.fromPersistence(cachedData);
}
// 从数据库查找
const query = `
SELECT u.*, p.first_name, p.last_name, p.hashed_password, p.avatar_url
FROM users u
LEFT JOIN user_profiles p ON u.id = p.user_id
WHERE u.id = $1 AND u.deleted_at IS NULL
`;
const result = await this.db.query(query, [id.value]);
if (result.rows.length === 0) {
return null;
}
const userData = this.mapRowToUserData(result.rows[0]);
const user = User.fromPersistence(userData);
// 缓存结果
await this.cache.set(cacheKey, userData, 3600); // 1小时
this.logger.debug('User found in database', { userId: id.value });
return user;
} catch (error) {
this.logger.error('Failed to find user by id', { error, userId: id.value });
throw new RepositoryError('Failed to find user', error);
}
}
async findByEmail(email: Email): Promise<User | null> {
try {
const query = `
SELECT u.*, p.first_name, p.last_name, p.hashed_password, p.avatar_url
FROM users u
LEFT JOIN user_profiles p ON u.id = p.user_id
WHERE LOWER(u.email) = LOWER($1) AND u.deleted_at IS NULL
`;
const result = await this.db.query(query, [email.value]);
if (result.rows.length === 0) {
return null;
}
const userData = this.mapRowToUserData(result.rows[0]);
return User.fromPersistence(userData);
} catch (error) {
this.logger.error('Failed to find user by email', { error, email: email.value });
throw new RepositoryError('Failed to find user', error);
}
}
async save(user: User): Promise<void> {
const client = await this.db.getClient();
try {
await client.query('BEGIN');
const userData = user.toPersistence();
// 检查用户是否已存在
const existingUser = await this.findById(user.id);
if (existingUser) {
// 更新用户
await this.updateUser(client, userData);
} else {
// 创建新用户
await this.insertUser(client, userData);
}
await client.query('COMMIT');
// 清除缓存
const cacheKey = `user:${user.id.value}`;
await this.cache.delete(cacheKey);
this.logger.debug('User saved successfully', { userId: user.id.value });
} catch (error) {
await client.query('ROLLBACK');
this.logger.error('Failed to save user', { error, userId: user.id.value });
throw new RepositoryError('Failed to save user', error);
} finally {
client.release();
}
}
async delete(id: UserId): Promise<void> {
const client = await this.db.getClient();
try {
await client.query('BEGIN');
// 软删除
const query = `
UPDATE users
SET deleted_at = NOW(), updated_at = NOW()
WHERE id = $1 AND deleted_at IS NULL
`;
const result = await client.query(query, [id.value]);
if (result.rowCount === 0) {
throw new NotFoundError('User not found');
}
await client.query('COMMIT');
// 清除缓存
const cacheKey = `user:${id.value}`;
await this.cache.delete(cacheKey);
this.logger.debug('User deleted successfully', { userId: id.value });
} catch (error) {
await client.query('ROLLBACK');
this.logger.error('Failed to delete user', { error, userId: id.value });
throw new RepositoryError('Failed to delete user', error);
} finally {
client.release();
}
}
async findAll(criteria?: UserSearchCriteria): Promise<User[]> {
try {
const { query, params } = this.buildSearchQuery(criteria);
const result = await this.db.query(query, params);
return result.rows.map(row => {
const userData = this.mapRowToUserData(row);
return User.fromPersistence(userData);
});
} catch (error) {
this.logger.error('Failed to find users', { error, criteria });
throw new RepositoryError('Failed to find users', error);
}
}
async count(criteria?: UserSearchCriteria): Promise<number> {
try {
const { query, params } = this.buildCountQuery(criteria);
const result = await this.db.query(query, params);
return parseInt(result.rows[0].count, 10);
} catch (error) {
this.logger.error('Failed to count users', { error, criteria });
throw new RepositoryError('Failed to count users', error);
}
}
private async insertUser(client: DatabaseClient, userData: UserPersistenceData): Promise<void> {
// 插入用户基本信息
const userQuery = `
INSERT INTO users (id, email, status, created_at, updated_at)
VALUES ($1, $2, $3, $4, $5)
`;
await client.query(userQuery, [
userData.id,
userData.email,
userData.status,
userData.createdAt,
userData.updatedAt
]);
// 插入用户档案
const profileQuery = `
INSERT INTO user_profiles (user_id, first_name, last_name, hashed_password, avatar_url)
VALUES ($1, $2, $3, $4, $5)
`;
await client.query(profileQuery, [
userData.id,
userData.profile.firstName,
userData.profile.lastName,
userData.profile.hashedPassword,
userData.profile.avatarUrl
]);
}
private async updateUser(client: DatabaseClient, userData: UserPersistenceData): Promise<void> {
// 更新用户基本信息
const userQuery = `
UPDATE users
SET email = $2, status = $3, updated_at = $4
WHERE id = $1
`;
await client.query(userQuery, [
userData.id,
userData.email,
userData.status,
userData.updatedAt
]);
// 更新用户档案
const profileQuery = `
UPDATE user_profiles
SET first_name = $2, last_name = $3, hashed_password = $4, avatar_url = $5
WHERE user_id = $1
`;
await client.query(profileQuery, [
userData.id,
userData.profile.firstName,
userData.profile.lastName,
userData.profile.hashedPassword,
userData.profile.avatarUrl
]);
}
private buildSearchQuery(criteria?: UserSearchCriteria): { query: string; params: any[] } {
let query = `
SELECT u.*, p.first_name, p.last_name, p.hashed_password, p.avatar_url
FROM users u
LEFT JOIN user_profiles p ON u.id = p.user_id
WHERE u.deleted_at IS NULL
`;
const params: any[] = [];
let paramIndex = 1;
if (criteria?.status) {
query += ` AND u.status = $${paramIndex}`;
params.push(criteria.status.value);
paramIndex++;
}
if (criteria?.emailDomain) {
query += ` AND u.email LIKE $${paramIndex}`;
params.push(`%@${criteria.emailDomain}`);
paramIndex++;
}
if (criteria?.createdAfter) {
query += ` AND u.created_at >= $${paramIndex}`;
params.push(criteria.createdAfter.toISOString());
paramIndex++;
}
if (criteria?.createdBefore) {
query += ` AND u.created_at <= $${paramIndex}`;
params.push(criteria.createdBefore.toISOString());
paramIndex++;
}
query += ' ORDER BY u.created_at DESC';
if (criteria?.limit) {
query += ` LIMIT $${paramIndex}`;
params.push(criteria.limit);
paramIndex++;
}
if (criteria?.offset) {
query += ` OFFSET $${paramIndex}`;
params.push(criteria.offset);
paramIndex++;
}
return { query, params };
}
private buildCountQuery(criteria?: UserSearchCriteria): { query: string; params: any[] } {
let query = `
SELECT COUNT(*) as count
FROM users u
WHERE u.deleted_at IS NULL
`;
const params: any[] = [];
let paramIndex = 1;
if (criteria?.status) {
query += ` AND u.status = $${paramIndex}`;
params.push(criteria.status.value);
paramIndex++;
}
if (criteria?.emailDomain) {
query += ` AND u.email LIKE $${paramIndex}`;
params.push(`%@${criteria.emailDomain}`);
paramIndex++;
}
if (criteria?.createdAfter) {
query += ` AND u.created_at >= $${paramIndex}`;
params.push(criteria.createdAfter.toISOString());
paramIndex++;
}
if (criteria?.createdBefore) {
query += ` AND u.created_at <= $${paramIndex}`;
params.push(criteria.createdBefore.toISOString());
paramIndex++;
}
return { query, params };
}
private mapRowToUserData(row: any): UserPersistenceData {
return {
id: row.id,
email: row.email,
status: row.status,
profile: {
firstName: row.first_name,
lastName: row.last_name,
hashedPassword: row.hashed_password,
avatarUrl: row.avatar_url
},
createdAt: row.created_at,
updatedAt: row.updated_at
};
}
}
本章练习
SOLID原则:
- 重构现有代码以遵循SOLID原则
- 识别和修复违反原则的代码
- 设计符合原则的新功能
领域驱动设计:
- 设计领域模型和值对象
- 实现聚合根和领域服务
- 定义和处理领域事件
微服务架构:
- 设计服务拆分策略
- 实现服务间通信
- 处理分布式事务
数据访问层:
- 实现Repository模式
- 设计缓存策略
- 优化数据库查询
本章总结
本章介绍了企业级TypeScript应用开发的核心概念:
- 架构设计原则:SOLID原则和领域驱动设计
- 微服务架构:服务拆分和通信策略
- 数据访问层:Repository模式和缓存设计
- 最佳实践:代码组织和错误处理
下一章将介绍TypeScript的最新特性和未来发展趋势。