enterprise-architecture-patterns
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseEnterprise Architecture Patterns
企业架构模式
A comprehensive skill for mastering enterprise architecture patterns, distributed systems design, and scalable application development. This skill covers strategic and tactical patterns for building robust, maintainable, and scalable enterprise systems.
这是一份掌握企业架构模式、分布式系统设计和可扩展应用开发的全面指南。本指南涵盖了构建稳健、可维护且可扩展的企业系统所需的战略与战术模式。
When to Use This Skill
何时使用本指南
Use this skill when:
- Designing microservices architectures for distributed systems
- Implementing domain-driven design (DDD) in complex business domains
- Building event-driven architectures with event sourcing and CQRS
- Designing saga patterns for distributed transactions
- Implementing API gateways and service mesh architectures
- Scaling applications horizontally and vertically
- Building resilient systems with fault tolerance
- Migrating monoliths to microservices
- Designing multi-tenant SaaS architectures
- Implementing backend-for-frontend (BFF) patterns
- Building real-time systems with event streaming
- Architecting cloud-native applications
在以下场景中使用本指南:
- 为分布式系统设计微服务架构
- 在复杂业务领域中实现领域驱动设计(DDD)
- 构建基于事件溯源和CQRS的事件驱动架构
- 为分布式事务设计Saga模式
- 实现API网关和服务网格架构
- 对应用进行水平和垂直扩展
- 构建具备容错能力的弹性系统
- 将单体应用迁移至微服务
- 设计多租户SaaS架构
- 实现Backend-for-Frontend(BFF)模式
- 构建基于事件流的实时系统
- 设计云原生应用
Core Architectural Concepts
核心架构概念
System Design Fundamentals
系统设计基础
Separation of Concerns
Divide systems into distinct sections where each section addresses a separate concern, reducing coupling and increasing cohesion.
Modularity
Design systems as collections of independent modules that can be developed, tested, and deployed separately.
Abstraction
Hide complex implementation details behind simple interfaces, making systems easier to understand and modify.
Scalability Dimensions
- Horizontal scaling: Add more machines/instances
- Vertical scaling: Add more resources to existing machines
- Data scaling: Partition data across multiple stores
- Functional scaling: Decompose by business capability
Consistency Models
- Strong consistency: All nodes see the same data at the same time
- Eventual consistency: All nodes will eventually see the same data
- Causal consistency: Related operations see consistent state
- Read-your-writes consistency: Users see their own updates immediately
CAP Theorem
In distributed systems, you can only guarantee two of three properties:
- Consistency: All nodes see the same data
- Availability: Every request receives a response
- Partition tolerance: System continues despite network failures
Distributed Computing Fallacies
- The network is reliable
- Latency is zero
- Bandwidth is infinite
- The network is secure
- Topology doesn't change
- There is one administrator
- Transport cost is zero
- The network is homogeneous
关注点分离
将系统划分为不同的模块,每个模块负责独立的关注点,降低耦合度并提高内聚性。
模块化
将系统设计为一组独立模块的集合,每个模块可单独开发、测试和部署。
抽象
通过简单的接口隐藏复杂的实现细节,使系统更易于理解和修改。
可扩展性维度
- 水平扩展:添加更多机器/实例
- 垂直扩展:为现有机器增加更多资源
- 数据扩展:在多个存储间分区存储数据
- 功能扩展:按业务能力分解系统
一致性模型
- 强一致性:所有节点在同一时间看到相同的数据
- 最终一致性:所有节点最终会看到相同的数据
- 因果一致性:相关操作能看到一致的状态
- 读己写一致性:用户能立即看到自己的更新
CAP定理
在分布式系统中,只能保证以下三个属性中的两个:
- 一致性(Consistency):所有节点看到相同的数据
- 可用性(Availability):每个请求都能收到响应
- 分区容错性(Partition tolerance):即使网络出现分区,系统仍能继续运行
分布式计算谬误
- 网络是可靠的
- 延迟为零
- 带宽无限
- 网络是安全的
- 拓扑结构不会改变
- 只有一个管理员
- 传输成本为零
- 网络是同质的
Domain-Driven Design (DDD)
领域驱动设计(DDD)
Strategic Design Patterns
战略设计模式
Bounded Context
限界上下文
A bounded context is an explicit boundary within which a domain model is consistent and valid. It defines the scope where particular terms, definitions, and rules apply.
Key Principles:
- Each bounded context has its own ubiquitous language
- Models within a context are consistent
- Cross-context integration requires translation
- Contexts align with business capabilities
Implementation:
typescript
// Example: E-commerce system with multiple bounded contexts
// Sales Context
namespace Sales {
class Customer {
customerId: string;
email: string;
orderHistory: Order[];
placeOrder(order: Order): void {
// Sales-specific logic
}
}
}
// Billing Context
namespace Billing {
class Customer {
customerId: string;
paymentMethods: PaymentMethod[];
invoices: Invoice[];
processPayment(invoice: Invoice): void {
// Billing-specific logic
}
}
}
// Different models for Customer in different contextsContext Mapping Patterns:
-
Shared Kernel: Two contexts share a subset of the domain model
- Use when: Teams are closely coordinated
- Risk: Changes affect multiple contexts
-
Customer-Supplier: Downstream context depends on upstream
- Use when: Clear dependency direction exists
- Pattern: Upstream provides defined API
-
Conformist: Downstream conforms to upstream model
- Use when: Upstream is external/unchangeable
- Pattern: Adapt to external API
-
Anti-Corruption Layer: Translate between contexts
- Use when: Protecting from legacy or external systems
- Pattern: Adapter/facade to translate models
-
Separate Ways: Contexts are completely independent
- Use when: No integration needed
- Pattern: Duplicate functionality if necessary
-
Open Host Service: Well-defined protocol for integration
- Use when: Multiple consumers need access
- Pattern: REST API, GraphQL, gRPC
-
Published Language: Shared, well-documented language
- Use when: Industry standards exist
- Pattern: XML schemas, JSON schemas, OpenAPI
限界上下文是一个明确的边界,在该边界内领域模型保持一致且有效。它定义了特定术语、定义和规则适用的范围。
核心原则:
- 每个限界上下文都有自己的通用语言
- 上下文内的模型保持一致
- 跨上下文集成需要进行转换
- 上下文与业务能力对齐
实现:
typescript
// Example: E-commerce system with multiple bounded contexts
// Sales Context
namespace Sales {
class Customer {
customerId: string;
email: string;
orderHistory: Order[];
placeOrder(order: Order): void {
// Sales-specific logic
}
}
}
// Billing Context
namespace Billing {
class Customer {
customerId: string;
paymentMethods: PaymentMethod[];
invoices: Invoice[];
processPayment(invoice: Invoice): void {
// Billing-specific logic
}
}
}
// Different models for Customer in different contexts上下文映射模式:
-
共享内核:两个上下文共享领域模型的一个子集
- 使用场景:团队协作紧密
- 风险:变更会影响多个上下文
-
客户-供应商:下游上下文依赖上游上下文
- 使用场景:存在明确的依赖方向
- 模式:上游提供定义好的API
-
遵循者:下游上下文遵循上游模型
- 使用场景:上游是外部系统或不可变更
- 模式:适配外部API
-
防腐层:在上下文之间进行转换
- 使用场景:保护系统免受遗留系统或外部系统的影响
- 模式:使用适配器/外观模式转换模型
-
独立方式:上下文完全独立
- 使用场景:无需集成
- 模式:必要时复制功能
-
开放主机服务:定义良好的集成协议
- 使用场景:多个消费者需要访问
- 模式:REST API、GraphQL、gRPC
-
发布语言:共享的、文档完善的语言
- 使用场景:存在行业标准
- 模式:XML Schema、JSON Schema、OpenAPI
Ubiquitous Language
通用语言
A shared vocabulary between developers and domain experts used consistently in code, documentation, and conversations.
Building Ubiquitous Language:
typescript
// Bad: Generic technical terms
class DataProcessor {
processData(data: any): void {
// Unclear what this does in business terms
}
}
// Good: Business domain terms
class OrderFulfillment {
fulfillOrder(order: Order): void {
this.pickItems(order.items);
this.packForShipment(order);
this.scheduleDelivery(order);
}
private pickItems(items: OrderItem[]): void {
// Business logic using domain language
}
}开发人员和领域专家之间共享的词汇表,在代码、文档和沟通中保持一致。
构建通用语言:
typescript
// Bad: Generic technical terms
class DataProcessor {
processData(data: any): void {
// Unclear what this does in business terms
}
}
// Good: Business domain terms
class OrderFulfillment {
fulfillOrder(order: Order): void {
this.pickItems(order.items);
this.packForShipment(order);
this.scheduleDelivery(order);
}
private pickItems(items: OrderItem[]): void {
// Business logic using domain language
}
}Tactical Design Patterns
战术设计模式
Entities
实体
Objects with unique identity that persist over time, tracking continuity and lifecycle.
Characteristics:
- Unique identifier (ID)
- Mutable state
- Lifecycle (created, modified, deleted)
- Equality based on identity, not attributes
Implementation:
typescript
class Order {
private readonly orderId: string;
private orderItems: OrderItem[];
private status: OrderStatus;
private orderDate: Date;
private customerId: string;
constructor(orderId: string, customerId: string) {
this.orderId = orderId;
this.customerId = customerId;
this.orderItems = [];
this.status = OrderStatus.Draft;
this.orderDate = new Date();
}
// Business behavior
addItem(product: Product, quantity: number): void {
if (this.status !== OrderStatus.Draft) {
throw new Error("Cannot modify confirmed order");
}
this.orderItems.push(new OrderItem(product, quantity));
}
confirm(): void {
if (this.orderItems.length === 0) {
throw new Error("Cannot confirm empty order");
}
this.status = OrderStatus.Confirmed;
}
// Identity-based equality
equals(other: Order): boolean {
return this.orderId === other.orderId;
}
}具有唯一标识的对象,会随时间持久存在,跟踪连续性和生命周期。
特征:
- 唯一标识符(ID)
- 可变状态
- 生命周期(创建、修改、删除)
- 基于标识而非属性判断相等性
实现:
typescript
class Order {
private readonly orderId: string;
private orderItems: OrderItem[];
private status: OrderStatus;
private orderDate: Date;
private customerId: string;
constructor(orderId: string, customerId: string) {
this.orderId = orderId;
this.customerId = customerId;
this.orderItems = [];
this.status = OrderStatus.Draft;
this.orderDate = new Date();
}
// Business behavior
addItem(product: Product, quantity: number): void {
if (this.status !== OrderStatus.Draft) {
throw new Error("Cannot modify confirmed order");
}
this.orderItems.push(new OrderItem(product, quantity));
}
confirm(): void {
if (this.orderItems.length === 0) {
throw new Error("Cannot confirm empty order");
}
this.status = OrderStatus.Confirmed;
}
// Identity-based equality
equals(other: Order): boolean {
return this.orderId === other.orderId;
}
}Value Objects
值对象
Immutable objects defined by their attributes rather than identity, representing descriptive aspects of the domain.
Characteristics:
- No unique identifier
- Immutable (cannot change state)
- Equality based on all attributes
- Often passed by value
- Can be shared safely
Implementation:
typescript
class Money {
readonly amount: number;
readonly currency: string;
constructor(amount: number, currency: string) {
if (amount < 0) {
throw new Error("Amount cannot be negative");
}
this.amount = amount;
this.currency = currency;
}
// Operations return new instances
add(other: Money): Money {
if (this.currency !== other.currency) {
throw new Error("Cannot add different currencies");
}
return new Money(this.amount + other.amount, this.currency);
}
multiply(factor: number): Money {
return new Money(this.amount * factor, this.currency);
}
// Value-based equality
equals(other: Money): boolean {
return this.amount === other.amount &&
this.currency === other.currency;
}
}
class Address {
readonly street: string;
readonly city: string;
readonly state: string;
readonly zipCode: string;
readonly country: string;
constructor(
street: string,
city: string,
state: string,
zipCode: string,
country: string
) {
this.street = street;
this.city = city;
this.state = state;
this.zipCode = zipCode;
this.country = country;
}
equals(other: Address): boolean {
return this.street === other.street &&
this.city === other.city &&
this.state === other.state &&
this.zipCode === other.zipCode &&
this.country === other.country;
}
}不可变对象,由其属性而非标识定义,代表领域的描述性方面。
特征:
- 无唯一标识符
- 不可变(状态无法更改)
- 基于所有属性判断相等性
- 通常按值传递
- 可安全共享
实现:
typescript
class Money {
readonly amount: number;
readonly currency: string;
constructor(amount: number, currency: string) {
if (amount < 0) {
throw new Error("Amount cannot be negative");
}
this.amount = amount;
this.currency = currency;
}
// Operations return new instances
add(other: Money): Money {
if (this.currency !== other.currency) {
throw new Error("Cannot add different currencies");
}
return new Money(this.amount + other.amount, this.currency);
}
multiply(factor: number): Money {
return new Money(this.amount * factor, this.currency);
}
// Value-based equality
equals(other: Money): boolean {
return this.amount === other.amount &&
this.currency === other.currency;
}
}
class Address {
readonly street: string;
readonly city: string;
readonly state: string;
readonly zipCode: string;
readonly country: string;
constructor(
street: string,
city: string,
state: string,
zipCode: string,
country: string
) {
this.street = street;
this.city = city;
this.state = state;
this.zipCode = zipCode;
this.country = country;
}
equals(other: Address): boolean {
return this.street === other.street &&
this.city === other.city &&
this.state === other.state &&
this.zipCode === other.zipCode &&
this.country === other.country;
}
}Aggregates
聚合
Clusters of entities and value objects with clear consistency boundaries, accessed through a single root entity.
Key Principles:
- One aggregate = one transaction boundary
- External references only to aggregate root
- Root enforces all invariants
- Small aggregates perform better
- Eventual consistency between aggregates
Design Rules:
- Model true invariants in consistency boundaries
- Design small aggregates
- Reference other aggregates by identity only
- Update other aggregates using eventual consistency
- Use repositories to retrieve aggregates
Implementation:
typescript
// Aggregate Root
class Order {
private readonly orderId: string;
private readonly customerId: string; // Reference by ID only
private orderItems: OrderItem[] = [];
private shippingAddress: Address;
private status: OrderStatus;
private totalAmount: Money;
constructor(orderId: string, customerId: string, shippingAddress: Address) {
this.orderId = orderId;
this.customerId = customerId;
this.shippingAddress = shippingAddress;
this.status = OrderStatus.Draft;
this.totalAmount = new Money(0, "USD");
}
// Public methods enforce invariants
addItem(product: Product, quantity: number): void {
if (this.status !== OrderStatus.Draft) {
throw new Error("Cannot modify confirmed order");
}
if (quantity <= 0) {
throw new Error("Quantity must be positive");
}
const existingItem = this.findItem(product.id);
if (existingItem) {
existingItem.increaseQuantity(quantity);
} else {
this.orderItems.push(new OrderItem(product, quantity));
}
this.recalculateTotal();
}
removeItem(productId: string): void {
if (this.status !== OrderStatus.Draft) {
throw new Error("Cannot modify confirmed order");
}
this.orderItems = this.orderItems.filter(
item => item.productId !== productId
);
this.recalculateTotal();
}
confirm(): void {
if (this.orderItems.length === 0) {
throw new Error("Cannot confirm empty order");
}
if (!this.shippingAddress) {
throw new Error("Shipping address required");
}
this.status = OrderStatus.Confirmed;
}
private recalculateTotal(): void {
this.totalAmount = this.orderItems.reduce(
(total, item) => total.add(item.subtotal),
new Money(0, "USD")
);
}
private findItem(productId: string): OrderItem | undefined {
return this.orderItems.find(item => item.productId === productId);
}
// Getters for read-only access
get id(): string { return this.orderId; }
get total(): Money { return this.totalAmount; }
get items(): readonly OrderItem[] { return this.orderItems; }
}
// Entity within aggregate
class OrderItem {
readonly productId: string;
readonly productName: string;
readonly unitPrice: Money;
private quantity: number;
constructor(product: Product, quantity: number) {
this.productId = product.id;
this.productName = product.name;
this.unitPrice = product.price;
this.quantity = quantity;
}
increaseQuantity(amount: number): void {
this.quantity += amount;
}
get subtotal(): Money {
return this.unitPrice.multiply(this.quantity);
}
}实体和值对象的集群,具有明确的一致性边界,通过单个根实体访问。
核心原则:
- 一个聚合 = 一个事务边界
- 外部仅引用聚合根
- 根实体强制执行所有不变量
- 小型聚合性能更优
- 聚合之间采用最终一致性
设计规则:
- 在一致性边界内建模真正的不变量
- 设计小型聚合
- 仅通过ID引用其他聚合
- 使用最终一致性更新其他聚合
- 使用仓库获取聚合
实现:
typescript
// Aggregate Root
class Order {
private readonly orderId: string;
private readonly customerId: string; // Reference by ID only
private orderItems: OrderItem[] = [];
private shippingAddress: Address;
private status: OrderStatus;
private totalAmount: Money;
constructor(orderId: string, customerId: string, shippingAddress: Address) {
this.orderId = orderId;
this.customerId = customerId;
this.shippingAddress = shippingAddress;
this.status = OrderStatus.Draft;
this.totalAmount = new Money(0, "USD");
}
// Public methods enforce invariants
addItem(product: Product, quantity: number): void {
if (this.status !== OrderStatus.Draft) {
throw new Error("Cannot modify confirmed order");
}
if (quantity <= 0) {
throw new Error("Quantity must be positive");
}
const existingItem = this.findItem(product.id);
if (existingItem) {
existingItem.increaseQuantity(quantity);
} else {
this.orderItems.push(new OrderItem(product, quantity));
}
this.recalculateTotal();
}
removeItem(productId: string): void {
if (this.status !== OrderStatus.Draft) {
throw new Error("Cannot modify confirmed order");
}
this.orderItems = this.orderItems.filter(
item => item.productId !== productId
);
this.recalculateTotal();
}
confirm(): void {
if (this.orderItems.length === 0) {
throw new Error("Cannot confirm empty order");
}
if (!this.shippingAddress) {
throw new Error("Shipping address required");
}
this.status = OrderStatus.Confirmed;
}
private recalculateTotal(): void {
this.totalAmount = this.orderItems.reduce(
(total, item) => total.add(item.subtotal),
new Money(0, "USD")
);
}
private findItem(productId: string): OrderItem | undefined {
return this.orderItems.find(item => item.productId === productId);
}
// Getters for read-only access
get id(): string { return this.orderId; }
get total(): Money { return this.totalAmount; }
get items(): readonly OrderItem[] { return this.orderItems; }
}
// Entity within aggregate
class OrderItem {
readonly productId: string;
readonly productName: string;
readonly unitPrice: Money;
private quantity: number;
constructor(product: Product, quantity: number) {
this.productId = product.id;
this.productName = product.name;
this.unitPrice = product.price;
this.quantity = quantity;
}
increaseQuantity(amount: number): void {
this.quantity += amount;
}
get subtotal(): Money {
return this.unitPrice.multiply(this.quantity);
}
}Domain Events
领域事件
Events that represent something significant that happened in the domain, enabling loose coupling and eventual consistency.
Characteristics:
- Past tense naming (OrderPlaced, PaymentProcessed)
- Immutable
- Include all necessary information
- Timestamped
- Often include aggregate ID
Implementation:
typescript
interface DomainEvent {
eventId: string;
occurredAt: Date;
aggregateId: string;
eventType: string;
}
class OrderPlacedEvent implements DomainEvent {
readonly eventId: string;
readonly occurredAt: Date;
readonly aggregateId: string;
readonly eventType = "OrderPlaced";
readonly orderId: string;
readonly customerId: string;
readonly totalAmount: Money;
readonly items: OrderItemDto[];
constructor(order: Order) {
this.eventId = generateId();
this.occurredAt = new Date();
this.aggregateId = order.id;
this.orderId = order.id;
this.customerId = order.customerId;
this.totalAmount = order.total;
this.items = order.items.map(item => ({
productId: item.productId,
quantity: item.quantity,
price: item.unitPrice
}));
}
}
// Domain event publisher
class DomainEventPublisher {
private handlers: Map<string, Function[]> = new Map();
subscribe(eventType: string, handler: Function): void {
if (!this.handlers.has(eventType)) {
this.handlers.set(eventType, []);
}
this.handlers.get(eventType)!.push(handler);
}
async publish(event: DomainEvent): Promise<void> {
const handlers = this.handlers.get(event.eventType) || [];
await Promise.all(handlers.map(handler => handler(event)));
}
}
// Usage
class OrderService {
constructor(
private orderRepository: OrderRepository,
private eventPublisher: DomainEventPublisher
) {}
async placeOrder(order: Order): Promise<void> {
order.confirm();
await this.orderRepository.save(order);
const event = new OrderPlacedEvent(order);
await this.eventPublisher.publish(event);
}
}代表领域中发生的重要事件,实现松耦合和最终一致性。
特征:
- 过去式命名(如OrderPlaced、PaymentProcessed)
- 不可变
- 包含所有必要信息
- 带时间戳
- 通常包含聚合ID
实现:
typescript
interface DomainEvent {
eventId: string;
occurredAt: Date;
aggregateId: string;
eventType: string;
}
class OrderPlacedEvent implements DomainEvent {
readonly eventId: string;
readonly occurredAt: Date;
readonly aggregateId: string;
readonly eventType = "OrderPlaced";
readonly orderId: string;
readonly customerId: string;
readonly totalAmount: Money;
readonly items: OrderItemDto[];
constructor(order: Order) {
this.eventId = generateId();
this.occurredAt = new Date();
this.aggregateId = order.id;
this.orderId = order.id;
this.customerId = order.customerId;
this.totalAmount = order.total;
this.items = order.items.map(item => ({
productId: item.productId,
quantity: item.quantity,
price: item.unitPrice
}));
}
}
// Domain event publisher
class DomainEventPublisher {
private handlers: Map<string, Function[]> = new Map();
subscribe(eventType: string, handler: Function): void {
if (!this.handlers.has(eventType)) {
this.handlers.set(eventType, []);
}
this.handlers.get(eventType)!.push(handler);
}
async publish(event: DomainEvent): Promise<void> {
const handlers = this.handlers.get(event.eventType) || [];
await Promise.all(handlers.map(handler => handler(event)));
}
}
// Usage
class OrderService {
constructor(
private orderRepository: OrderRepository,
private eventPublisher: DomainEventPublisher
) {}
async placeOrder(order: Order): Promise<void> {
order.confirm();
await this.orderRepository.save(order);
const event = new OrderPlacedEvent(order);
await this.eventPublisher.publish(event);
}
}Repositories
仓库
Abstraction for accessing aggregates, providing collection-like interface while hiding persistence details.
Principles:
- One repository per aggregate root
- Collection-oriented interface
- Hide database implementation
- Return fully-formed aggregates
- Support querying by ID and business criteria
Implementation:
typescript
interface OrderRepository {
save(order: Order): Promise<void>;
findById(orderId: string): Promise<Order | null>;
findByCustomer(customerId: string): Promise<Order[]>;
findByStatus(status: OrderStatus): Promise<Order[]>;
delete(orderId: string): Promise<void>;
}
class OrderRepositoryImpl implements OrderRepository {
constructor(private db: Database) {}
async save(order: Order): Promise<void> {
const data = this.toDataModel(order);
await this.db.orders.upsert(data);
}
async findById(orderId: string): Promise<Order | null> {
const data = await this.db.orders.findOne({ id: orderId });
if (!data) return null;
return this.toDomainModel(data);
}
async findByCustomer(customerId: string): Promise<Order[]> {
const results = await this.db.orders.find({ customerId });
return results.map(data => this.toDomainModel(data));
}
async findByStatus(status: OrderStatus): Promise<Order[]> {
const results = await this.db.orders.find({ status });
return results.map(data => this.toDomainModel(data));
}
async delete(orderId: string): Promise<void> {
await this.db.orders.delete({ id: orderId });
}
private toDataModel(order: Order): any {
// Convert domain model to database model
return {
id: order.id,
customerId: order.customerId,
items: order.items.map(item => ({
productId: item.productId,
quantity: item.quantity,
price: item.unitPrice.amount
})),
total: order.total.amount,
status: order.status
};
}
private toDomainModel(data: any): Order {
// Reconstruct domain model from database data
const order = new Order(data.id, data.customerId, data.shippingAddress);
// Restore items and state
return order;
}
}用于访问聚合的抽象,提供类似集合的接口,同时隐藏持久化细节。
原则:
- 每个聚合根对应一个仓库
- 面向集合的接口
- 隐藏数据库实现
- 返回完整的聚合
- 支持按ID和业务条件查询
实现:
typescript
interface OrderRepository {
save(order: Order): Promise<void>;
findById(orderId: string): Promise<Order | null>;
findByCustomer(customerId: string): Promise<Order[]>;
findByStatus(status: OrderStatus): Promise<Order[]>;
delete(orderId: string): Promise<void>;
}
class OrderRepositoryImpl implements OrderRepository {
constructor(private db: Database) {}
async save(order: Order): Promise<void> {
const data = this.toDataModel(order);
await this.db.orders.upsert(data);
}
async findById(orderId: string): Promise<Order | null> {
const data = await this.db.orders.findOne({ id: orderId });
if (!data) return null;
return this.toDomainModel(data);
}
async findByCustomer(customerId: string): Promise<Order[]> {
const results = await this.db.orders.find({ customerId });
return results.map(data => this.toDomainModel(data));
}
async findByStatus(status: OrderStatus): Promise<Order[]> {
const results = await this.db.orders.find({ status });
return results.map(data => this.toDomainModel(data));
}
async delete(orderId: string): Promise<void> {
await this.db.orders.delete({ id: orderId });
}
private toDataModel(order: Order): any {
// Convert domain model to database model
return {
id: order.id,
customerId: order.customerId,
items: order.items.map(item => ({
productId: item.productId,
quantity: item.quantity,
price: item.unitPrice.amount
})),
total: order.total.amount,
status: order.status
};
}
private toDomainModel(data: any): Order {
// Reconstruct domain model from database data
const order = new Order(data.id, data.customerId, data.shippingAddress);
// Restore items and state
return order;
}
}Domain Services
领域服务
Operations that don't naturally belong to any entity or value object, encapsulating domain logic that involves multiple aggregates.
When to Use:
- Logic spans multiple aggregates
- Operation is a significant domain concept
- Behavior doesn't fit naturally in entity or value object
Implementation:
typescript
class PricingService {
calculateOrderTotal(
items: OrderItem[],
customer: Customer,
promotions: Promotion[]
): Money {
let total = items.reduce(
(sum, item) => sum.add(item.subtotal),
new Money(0, "USD")
);
// Apply customer discount
if (customer.isPremium) {
total = total.multiply(0.9); // 10% discount
}
// Apply promotions
for (const promo of promotions) {
total = promo.apply(total);
}
return total;
}
}
class TransferService {
transfer(
fromAccount: Account,
toAccount: Account,
amount: Money
): void {
if (!fromAccount.canWithdraw(amount)) {
throw new Error("Insufficient funds");
}
fromAccount.withdraw(amount);
toAccount.deposit(amount);
}
}不属于任何实体或值对象的操作,封装涉及多个聚合的领域逻辑。
使用场景:
- 逻辑跨越多个聚合
- 操作是重要的领域概念
- 行为不适合放在实体或值对象中
实现:
typescript
class PricingService {
calculateOrderTotal(
items: OrderItem[],
customer: Customer,
promotions: Promotion[]
): Money {
let total = items.reduce(
(sum, item) => sum.add(item.subtotal),
new Money(0, "USD")
);
// Apply customer discount
if (customer.isPremium) {
total = total.multiply(0.9); // 10% discount
}
// Apply promotions
for (const promo of promotions) {
total = promo.apply(total);
}
return total;
}
}
class TransferService {
transfer(
fromAccount: Account,
toAccount: Account,
amount: Money
): void {
if (!fromAccount.canWithdraw(amount)) {
throw new Error("Insufficient funds");
}
fromAccount.withdraw(amount);
toAccount.deposit(amount);
}
}Event Sourcing
事件溯源
Event sourcing persists the state of a system as a sequence of events rather than storing current state. The current state is derived by replaying events.
事件溯源通过存储一系列事件而非当前状态来持久化系统状态,当前状态可通过重放事件推导得出。
Core Concepts
核心概念
Event Store
Append-only log of all events that have occurred in the system.
Event Stream
Sequence of events for a specific aggregate, ordered by time.
Projection
Read model built by processing events, optimized for queries.
Snapshot
Cached state at a point in time to avoid replaying all events.
事件存储
所有已发生事件的追加式日志。
事件流
特定聚合的事件序列,按时间排序。
投影
通过处理事件构建的读模型,针对查询进行优化。
快照
缓存某一时刻的聚合状态,避免重放所有事件。
Implementation
实现
typescript
// Event interface
interface Event {
eventId: string;
eventType: string;
aggregateId: string;
aggregateType: string;
version: number;
timestamp: Date;
data: any;
metadata?: any;
}
// Account aggregate with event sourcing
class Account {
private accountId: string;
private balance: number = 0;
private isActive: boolean = true;
private version: number = 0;
private uncommittedEvents: Event[] = [];
constructor(accountId: string) {
this.accountId = accountId;
}
// Command handlers
open(initialBalance: number): void {
if (this.version > 0) {
throw new Error("Account already opened");
}
this.applyEvent({
eventType: "AccountOpened",
data: { accountId: this.accountId, initialBalance }
});
}
deposit(amount: number): void {
if (!this.isActive) {
throw new Error("Account is closed");
}
if (amount <= 0) {
throw new Error("Amount must be positive");
}
this.applyEvent({
eventType: "MoneyDeposited",
data: { amount }
});
}
withdraw(amount: number): void {
if (!this.isActive) {
throw new Error("Account is closed");
}
if (amount <= 0) {
throw new Error("Amount must be positive");
}
if (this.balance < amount) {
throw new Error("Insufficient funds");
}
this.applyEvent({
eventType: "MoneyWithdrawn",
data: { amount }
});
}
close(): void {
if (!this.isActive) {
throw new Error("Account already closed");
}
if (this.balance > 0) {
throw new Error("Cannot close account with positive balance");
}
this.applyEvent({
eventType: "AccountClosed",
data: {}
});
}
// Event application
private applyEvent(eventData: Partial<Event>): void {
const event: Event = {
eventId: generateId(),
eventType: eventData.eventType!,
aggregateId: this.accountId,
aggregateType: "Account",
version: this.version + 1,
timestamp: new Date(),
data: eventData.data,
metadata: eventData.metadata
};
this.apply(event);
this.uncommittedEvents.push(event);
}
// Event handlers (state mutations)
private apply(event: Event): void {
switch (event.eventType) {
case "AccountOpened":
this.balance = event.data.initialBalance;
this.isActive = true;
break;
case "MoneyDeposited":
this.balance += event.data.amount;
break;
case "MoneyWithdrawn":
this.balance -= event.data.amount;
break;
case "AccountClosed":
this.isActive = false;
break;
default:
throw new Error(`Unknown event type: ${event.eventType}`);
}
this.version = event.version;
}
// Replay events to rebuild state
static fromEvents(events: Event[]): Account {
if (events.length === 0) {
throw new Error("Cannot create account from empty event stream");
}
const account = new Account(events[0].aggregateId);
events.forEach(event => account.apply(event));
return account;
}
getUncommittedEvents(): Event[] {
return this.uncommittedEvents;
}
markEventsAsCommitted(): void {
this.uncommittedEvents = [];
}
}
// Event store interface
interface EventStore {
append(events: Event[]): Promise<void>;
getEvents(aggregateId: string, fromVersion?: number): Promise<Event[]>;
getAllEvents(fromTimestamp?: Date): Promise<Event[]>;
}
// Event store implementation
class InMemoryEventStore implements EventStore {
private events: Map<string, Event[]> = new Map();
private allEvents: Event[] = [];
async append(events: Event[]): Promise<void> {
for (const event of events) {
// Store in aggregate stream
if (!this.events.has(event.aggregateId)) {
this.events.set(event.aggregateId, []);
}
this.events.get(event.aggregateId)!.push(event);
// Store in global stream
this.allEvents.push(event);
}
}
async getEvents(
aggregateId: string,
fromVersion: number = 0
): Promise<Event[]> {
const events = this.events.get(aggregateId) || [];
return events.filter(e => e.version > fromVersion);
}
async getAllEvents(fromTimestamp?: Date): Promise<Event[]> {
if (!fromTimestamp) {
return this.allEvents;
}
return this.allEvents.filter(e => e.timestamp >= fromTimestamp);
}
}
// Repository with event sourcing
class EventSourcedAccountRepository {
constructor(private eventStore: EventStore) {}
async save(account: Account): Promise<void> {
const events = account.getUncommittedEvents();
if (events.length > 0) {
await this.eventStore.append(events);
account.markEventsAsCommitted();
}
}
async findById(accountId: string): Promise<Account | null> {
const events = await this.eventStore.getEvents(accountId);
if (events.length === 0) {
return null;
}
return Account.fromEvents(events);
}
}typescript
// Event interface
interface Event {
eventId: string;
eventType: string;
aggregateId: string;
aggregateType: string;
version: number;
timestamp: Date;
data: any;
metadata?: any;
}
// Account aggregate with event sourcing
class Account {
private accountId: string;
private balance: number = 0;
private isActive: boolean = true;
private version: number = 0;
private uncommittedEvents: Event[] = [];
constructor(accountId: string) {
this.accountId = accountId;
}
// Command handlers
open(initialBalance: number): void {
if (this.version > 0) {
throw new Error("Account already opened");
}
this.applyEvent({
eventType: "AccountOpened",
data: { accountId: this.accountId, initialBalance }
});
}
deposit(amount: number): void {
if (!this.isActive) {
throw new Error("Account is closed");
}
if (amount <= 0) {
throw new Error("Amount must be positive");
}
this.applyEvent({
eventType: "MoneyDeposited",
data: { amount }
});
}
withdraw(amount: number): void {
if (!this.isActive) {
throw new Error("Account is closed");
}
if (amount <= 0) {
throw new Error("Amount must be positive");
}
if (this.balance < amount) {
throw new Error("Insufficient funds");
}
this.applyEvent({
eventType: "MoneyWithdrawn",
data: { amount }
});
}
close(): void {
if (!this.isActive) {
throw new Error("Account already closed");
}
if (this.balance > 0) {
throw new Error("Cannot close account with positive balance");
}
this.applyEvent({
eventType: "AccountClosed",
data: {}
});
}
// Event application
private applyEvent(eventData: Partial<Event>): void {
const event: Event = {
eventId: generateId(),
eventType: eventData.eventType!,
aggregateId: this.accountId,
aggregateType: "Account",
version: this.version + 1,
timestamp: new Date(),
data: eventData.data,
metadata: eventData.metadata
};
this.apply(event);
this.uncommittedEvents.push(event);
}
// Event handlers (state mutations)
private apply(event: Event): void {
switch (event.eventType) {
case "AccountOpened":
this.balance = event.data.initialBalance;
this.isActive = true;
break;
case "MoneyDeposited":
this.balance += event.data.amount;
break;
case "MoneyWithdrawn":
this.balance -= event.data.amount;
break;
case "AccountClosed":
this.isActive = false;
break;
default:
throw new Error(`Unknown event type: ${event.eventType}`);
}
this.version = event.version;
}
// Replay events to rebuild state
static fromEvents(events: Event[]): Account {
if (events.length === 0) {
throw new Error("Cannot create account from empty event stream");
}
const account = new Account(events[0].aggregateId);
events.forEach(event => account.apply(event));
return account;
}
getUncommittedEvents(): Event[] {
return this.uncommittedEvents;
}
markEventsAsCommitted(): void {
this.uncommittedEvents = [];
}
}
// Event store interface
interface EventStore {
append(events: Event[]): Promise<void>;
getEvents(aggregateId: string, fromVersion?: number): Promise<Event[]>;
getAllEvents(fromTimestamp?: Date): Promise<Event[]>;
}
// Event store implementation
class InMemoryEventStore implements EventStore {
private events: Map<string, Event[]> = new Map();
private allEvents: Event[] = [];
async append(events: Event[]): Promise<void> {
for (const event of events) {
// Store in aggregate stream
if (!this.events.has(event.aggregateId)) {
this.events.set(event.aggregateId, []);
}
this.events.get(event.aggregateId)!.push(event);
// Store in global stream
this.allEvents.push(event);
}
}
async getEvents(
aggregateId: string,
fromVersion: number = 0
): Promise<Event[]> {
const events = this.events.get(aggregateId) || [];
return events.filter(e => e.version > fromVersion);
}
async getAllEvents(fromTimestamp?: Date): Promise<Event[]> {
if (!fromTimestamp) {
return this.allEvents;
}
return this.allEvents.filter(e => e.timestamp >= fromTimestamp);
}
}
// Repository with event sourcing
class EventSourcedAccountRepository {
constructor(private eventStore: EventStore) {}
async save(account: Account): Promise<void> {
const events = account.getUncommittedEvents();
if (events.length > 0) {
await this.eventStore.append(events);
account.markEventsAsCommitted();
}
}
async findById(accountId: string): Promise<Account | null> {
const events = await this.eventStore.getEvents(accountId);
if (events.length === 0) {
return null;
}
return Account.fromEvents(events);
}
}Snapshots
快照
Optimize performance by periodically saving aggregate state:
typescript
interface Snapshot {
aggregateId: string;
version: number;
timestamp: Date;
state: any;
}
class SnapshotStore {
private snapshots: Map<string, Snapshot> = new Map();
async save(snapshot: Snapshot): Promise<void> {
this.snapshots.set(snapshot.aggregateId, snapshot);
}
async getLatest(aggregateId: string): Promise<Snapshot | null> {
return this.snapshots.get(aggregateId) || null;
}
}
class EventSourcedAccountRepositoryWithSnapshots {
constructor(
private eventStore: EventStore,
private snapshotStore: SnapshotStore,
private snapshotInterval: number = 100
) {}
async save(account: Account): Promise<void> {
const events = account.getUncommittedEvents();
await this.eventStore.append(events);
account.markEventsAsCommitted();
// Create snapshot every N events
if (account.version % this.snapshotInterval === 0) {
await this.snapshotStore.save({
aggregateId: account.id,
version: account.version,
timestamp: new Date(),
state: account.toSnapshot()
});
}
}
async findById(accountId: string): Promise<Account | null> {
// Try to load from snapshot
const snapshot = await this.snapshotStore.getLatest(accountId);
let account: Account;
let fromVersion = 0;
if (snapshot) {
account = Account.fromSnapshot(snapshot.state);
fromVersion = snapshot.version;
} else {
account = new Account(accountId);
}
// Apply events after snapshot
const events = await this.eventStore.getEvents(accountId, fromVersion);
events.forEach(event => account.apply(event));
return account;
}
}通过定期保存聚合状态优化性能:
typescript
interface Snapshot {
aggregateId: string;
version: number;
timestamp: Date;
state: any;
}
class SnapshotStore {
private snapshots: Map<string, Snapshot> = new Map();
async save(snapshot: Snapshot): Promise<void> {
this.snapshots.set(snapshot.aggregateId, snapshot);
}
async getLatest(aggregateId: string): Promise<Snapshot | null> {
return this.snapshots.get(aggregateId) || null;
}
}
class EventSourcedAccountRepositoryWithSnapshots {
constructor(
private eventStore: EventStore,
private snapshotStore: SnapshotStore,
private snapshotInterval: number = 100
) {}
async save(account: Account): Promise<void> {
const events = account.getUncommittedEvents();
await this.eventStore.append(events);
account.markEventsAsCommitted();
// Create snapshot every N events
if (account.version % this.snapshotInterval === 0) {
await this.snapshotStore.save({
aggregateId: account.id,
version: account.version,
timestamp: new Date(),
state: account.toSnapshot()
});
}
}
async findById(accountId: string): Promise<Account | null> {
// Try to load from snapshot
const snapshot = await this.snapshotStore.getLatest(accountId);
let account: Account;
let fromVersion = 0;
if (snapshot) {
account = Account.fromSnapshot(snapshot.state);
fromVersion = snapshot.version;
} else {
account = new Account(accountId);
}
// Apply events after snapshot
const events = await this.eventStore.getEvents(accountId, fromVersion);
events.forEach(event => account.apply(event));
return account;
}
}CQRS (Command Query Responsibility Segregation)
CQRS(命令查询职责分离)
Separate read and write operations into different models, optimizing each for its specific use case.
将读操作和写操作分离为不同的模型,针对各自的使用场景进行优化。
Architecture
架构
Commands → Command Handlers → Aggregates → Events → Event Store
↓
Event Bus
↓
Projections → Read Models → Queries命令 → 命令处理器 → 聚合 → 事件 → 事件存储
↓
事件总线
↓
投影 → 读模型 → 查询Implementation
实现
typescript
// Commands (write operations)
interface Command {
commandId: string;
timestamp: Date;
}
class CreateAccountCommand implements Command {
commandId: string;
timestamp: Date;
accountId: string;
initialBalance: number;
constructor(accountId: string, initialBalance: number) {
this.commandId = generateId();
this.timestamp = new Date();
this.accountId = accountId;
this.initialBalance = initialBalance;
}
}
class DepositMoneyCommand implements Command {
commandId: string;
timestamp: Date;
accountId: string;
amount: number;
constructor(accountId: string, amount: number) {
this.commandId = generateId();
this.timestamp = new Date();
this.accountId = accountId;
this.amount = amount;
}
}
// Command handlers
class AccountCommandHandler {
constructor(
private repository: EventSourcedAccountRepository,
private eventBus: EventBus
) {}
async handle(command: Command): Promise<void> {
if (command instanceof CreateAccountCommand) {
await this.handleCreateAccount(command);
} else if (command instanceof DepositMoneyCommand) {
await this.handleDepositMoney(command);
}
}
private async handleCreateAccount(
command: CreateAccountCommand
): Promise<void> {
const account = new Account(command.accountId);
account.open(command.initialBalance);
await this.repository.save(account);
// Publish events
const events = account.getUncommittedEvents();
await this.eventBus.publish(events);
}
private async handleDepositMoney(
command: DepositMoneyCommand
): Promise<void> {
const account = await this.repository.findById(command.accountId);
if (!account) {
throw new Error("Account not found");
}
account.deposit(command.amount);
await this.repository.save(account);
const events = account.getUncommittedEvents();
await this.eventBus.publish(events);
}
}
// Read models (optimized for queries)
interface AccountReadModel {
accountId: string;
balance: number;
status: string;
lastActivity: Date;
transactionCount: number;
}
interface AccountSummaryReadModel {
accountId: string;
balance: number;
status: string;
}
// Projections (build read models from events)
class AccountProjection {
constructor(private db: ReadDatabase) {}
async handleEvent(event: Event): Promise<void> {
switch (event.eventType) {
case "AccountOpened":
await this.handleAccountOpened(event);
break;
case "MoneyDeposited":
await this.handleMoneyDeposited(event);
break;
case "MoneyWithdrawn":
await this.handleMoneyWithdrawn(event);
break;
case "AccountClosed":
await this.handleAccountClosed(event);
break;
}
}
private async handleAccountOpened(event: Event): Promise<void> {
await this.db.accounts.insert({
accountId: event.aggregateId,
balance: event.data.initialBalance,
status: "Active",
lastActivity: event.timestamp,
transactionCount: 0
});
}
private async handleMoneyDeposited(event: Event): Promise<void> {
await this.db.accounts.update(
{ accountId: event.aggregateId },
{
$inc: { balance: event.data.amount, transactionCount: 1 },
$set: { lastActivity: event.timestamp }
}
);
}
private async handleMoneyWithdrawn(event: Event): Promise<void> {
await this.db.accounts.update(
{ accountId: event.aggregateId },
{
$inc: { balance: -event.data.amount, transactionCount: 1 },
$set: { lastActivity: event.timestamp }
}
);
}
private async handleAccountClosed(event: Event): Promise<void> {
await this.db.accounts.update(
{ accountId: event.aggregateId },
{
$set: {
status: "Closed",
lastActivity: event.timestamp
}
}
);
}
}
// Query service (read-only)
class AccountQueryService {
constructor(private db: ReadDatabase) {}
async getAccount(accountId: string): Promise<AccountReadModel | null> {
return await this.db.accounts.findOne({ accountId });
}
async getAccountsByStatus(status: string): Promise<AccountSummaryReadModel[]> {
return await this.db.accounts.find(
{ status },
{ projection: { accountId: 1, balance: 1, status: 1 } }
);
}
async getHighBalanceAccounts(
minBalance: number
): Promise<AccountSummaryReadModel[]> {
return await this.db.accounts.find(
{ balance: { $gte: minBalance } },
{ projection: { accountId: 1, balance: 1, status: 1 } }
);
}
}
// Event bus for publishing events
class EventBus {
private subscribers: Map<string, Function[]> = new Map();
subscribe(eventType: string, handler: Function): void {
if (!this.subscribers.has(eventType)) {
this.subscribers.set(eventType, []);
}
this.subscribers.get(eventType)!.push(handler);
}
subscribeToAll(handler: Function): void {
this.subscribe("*", handler);
}
async publish(events: Event[]): Promise<void> {
for (const event of events) {
// Call specific handlers
const handlers = this.subscribers.get(event.eventType) || [];
await Promise.all(handlers.map(h => h(event)));
// Call wildcard handlers
const allHandlers = this.subscribers.get("*") || [];
await Promise.all(allHandlers.map(h => h(event)));
}
}
}typescript
// Commands (write operations)
interface Command {
commandId: string;
timestamp: Date;
}
class CreateAccountCommand implements Command {
commandId: string;
timestamp: Date;
accountId: string;
initialBalance: number;
constructor(accountId: string, initialBalance: number) {
this.commandId = generateId();
this.timestamp = new Date();
this.accountId = accountId;
this.initialBalance = initialBalance;
}
}
class DepositMoneyCommand implements Command {
commandId: string;
timestamp: Date;
accountId: string;
amount: number;
constructor(accountId: string, amount: number) {
this.commandId = generateId();
this.timestamp = new Date();
this.accountId = accountId;
this.amount = amount;
}
}
// Command handlers
class AccountCommandHandler {
constructor(
private repository: EventSourcedAccountRepository,
private eventBus: EventBus
) {}
async handle(command: Command): Promise<void> {
if (command instanceof CreateAccountCommand) {
await this.handleCreateAccount(command);
} else if (command instanceof DepositMoneyCommand) {
await this.handleDepositMoney(command);
}
}
private async handleCreateAccount(
command: CreateAccountCommand
): Promise<void> {
const account = new Account(command.accountId);
account.open(command.initialBalance);
await this.repository.save(account);
// Publish events
const events = account.getUncommittedEvents();
await this.eventBus.publish(events);
}
private async handleDepositMoney(
command: DepositMoneyCommand
): Promise<void> {
const account = await this.repository.findById(command.accountId);
if (!account) {
throw new Error("Account not found");
}
account.deposit(command.amount);
await this.repository.save(account);
const events = account.getUncommittedEvents();
await this.eventBus.publish(events);
}
}
// Read models (optimized for queries)
interface AccountReadModel {
accountId: string;
balance: number;
status: string;
lastActivity: Date;
transactionCount: number;
}
interface AccountSummaryReadModel {
accountId: string;
balance: number;
status: string;
}
// Projections (build read models from events)
class AccountProjection {
constructor(private db: ReadDatabase) {}
async handleEvent(event: Event): Promise<void> {
switch (event.eventType) {
case "AccountOpened":
await this.handleAccountOpened(event);
break;
case "MoneyDeposited":
await this.handleMoneyDeposited(event);
break;
case "MoneyWithdrawn":
await this.handleMoneyWithdrawn(event);
break;
case "AccountClosed":
await this.handleAccountClosed(event);
break;
}
}
private async handleAccountOpened(event: Event): Promise<void> {
await this.db.accounts.insert({
accountId: event.aggregateId,
balance: event.data.initialBalance,
status: "Active",
lastActivity: event.timestamp,
transactionCount: 0
});
}
private async handleMoneyDeposited(event: Event): Promise<void> {
await this.db.accounts.update(
{ accountId: event.aggregateId },
{
$inc: { balance: event.data.amount, transactionCount: 1 },
$set: { lastActivity: event.timestamp }
}
);
}
private async handleMoneyWithdrawn(event: Event): Promise<void> {
await this.db.accounts.update(
{ accountId: event.aggregateId },
{
$inc: { balance: -event.data.amount, transactionCount: 1 },
$set: { lastActivity: event.timestamp }
}
);
}
private async handleAccountClosed(event: Event): Promise<void> {
await this.db.accounts.update(
{ accountId: event.aggregateId },
{
$set: {
status: "Closed",
lastActivity: event.timestamp
}
}
);
}
}
// Query service (read-only)
class AccountQueryService {
constructor(private db: ReadDatabase) {}
async getAccount(accountId: string): Promise<AccountReadModel | null> {
return await this.db.accounts.findOne({ accountId });
}
async getAccountsByStatus(status: string): Promise<AccountSummaryReadModel[]> {
return await this.db.accounts.find(
{ status },
{ projection: { accountId: 1, balance: 1, status: 1 } }
);
}
async getHighBalanceAccounts(
minBalance: number
): Promise<AccountSummaryReadModel[]> {
return await this.db.accounts.find(
{ balance: { $gte: minBalance } },
{ projection: { accountId: 1, balance: 1, status: 1 } }
);
}
}
// Event bus for publishing events
class EventBus {
private subscribers: Map<string, Function[]> = new Map();
subscribe(eventType: string, handler: Function): void {
if (!this.subscribers.has(eventType)) {
this.subscribers.set(eventType, []);
}
this.subscribers.get(eventType)!.push(handler);
}
subscribeToAll(handler: Function): void {
this.subscribe("*", handler);
}
async publish(events: Event[]): Promise<void> {
for (const event of events) {
// Call specific handlers
const handlers = this.subscribers.get(event.eventType) || [];
await Promise.all(handlers.map(h => h(event)));
// Call wildcard handlers
const allHandlers = this.subscribers.get("*") || [];
await Promise.all(allHandlers.map(h => h(event)));
}
}
}Saga Pattern
Saga模式
Manage distributed transactions across multiple services using a sequence of local transactions coordinated by a saga.
通过一系列本地事务协调多个服务,管理分布式事务。
Orchestration-Based Saga
基于编排的Saga
A central orchestrator coordinates all saga participants.
typescript
// Saga state
enum SagaStatus {
Started = "Started",
Completed = "Completed",
Compensating = "Compensating",
Compensated = "Compensated",
Failed = "Failed"
}
interface SagaStep {
name: string;
action: () => Promise<void>;
compensation: () => Promise<void>;
}
class OrderSaga {
private sagaId: string;
private status: SagaStatus;
private completedSteps: string[] = [];
private currentStep: number = 0;
private steps: SagaStep[] = [
{
name: "CreateOrder",
action: async () => await this.createOrder(),
compensation: async () => await this.cancelOrder()
},
{
name: "ReserveInventory",
action: async () => await this.reserveInventory(),
compensation: async () => await this.releaseInventory()
},
{
name: "ProcessPayment",
action: async () => await this.processPayment(),
compensation: async () => await this.refundPayment()
},
{
name: "ArrangeShipment",
action: async () => await this.arrangeShipment(),
compensation: async () => await this.cancelShipment()
}
];
constructor(
private orderId: string,
private customerId: string,
private items: OrderItem[]
) {
this.sagaId = generateId();
this.status = SagaStatus.Started;
}
async execute(): Promise<void> {
try {
// Execute each step
for (let i = 0; i < this.steps.length; i++) {
this.currentStep = i;
const step = this.steps[i];
console.log(`Executing step: ${step.name}`);
await step.action();
this.completedSteps.push(step.name);
}
this.status = SagaStatus.Completed;
console.log("Saga completed successfully");
} catch (error) {
console.error(`Saga failed at step ${this.currentStep}:`, error);
await this.compensate();
}
}
private async compensate(): Promise<void> {
this.status = SagaStatus.Compensating;
console.log("Starting compensation");
// Compensate in reverse order
for (let i = this.completedSteps.length - 1; i >= 0; i--) {
const stepName = this.completedSteps[i];
const step = this.steps.find(s => s.name === stepName);
if (step) {
try {
console.log(`Compensating step: ${step.name}`);
await step.compensation();
} catch (error) {
console.error(`Compensation failed for ${step.name}:`, error);
// Log for manual intervention
}
}
}
this.status = SagaStatus.Compensated;
console.log("Compensation completed");
}
// Step implementations
private async createOrder(): Promise<void> {
await orderService.create({
orderId: this.orderId,
customerId: this.customerId,
items: this.items
});
}
private async cancelOrder(): Promise<void> {
await orderService.cancel(this.orderId);
}
private async reserveInventory(): Promise<void> {
await inventoryService.reserve(this.orderId, this.items);
}
private async releaseInventory(): Promise<void> {
await inventoryService.release(this.orderId);
}
private async processPayment(): Promise<void> {
const total = this.calculateTotal();
await paymentService.charge(this.customerId, total);
}
private async refundPayment(): Promise<void> {
const total = this.calculateTotal();
await paymentService.refund(this.customerId, total);
}
private async arrangeShipment(): Promise<void> {
await shippingService.createShipment(this.orderId);
}
private async cancelShipment(): Promise<void> {
await shippingService.cancelShipment(this.orderId);
}
private calculateTotal(): Money {
return this.items.reduce(
(sum, item) => sum.add(item.price.multiply(item.quantity)),
new Money(0, "USD")
);
}
}
// Orchestrator service
class SagaOrchestrator {
private activeSagas: Map<string, OrderSaga> = new Map();
async startOrderSaga(
orderId: string,
customerId: string,
items: OrderItem[]
): Promise<void> {
const saga = new OrderSaga(orderId, customerId, items);
this.activeSagas.set(saga.sagaId, saga);
try {
await saga.execute();
} finally {
this.activeSagas.delete(saga.sagaId);
}
}
getSagaStatus(sagaId: string): SagaStatus | null {
const saga = this.activeSagas.get(sagaId);
return saga ? saga.status : null;
}
}由中央编排器协调所有Saga参与者。
typescript
// Saga state
enum SagaStatus {
Started = "Started",
Completed = "Completed",
Compensating = "Compensating",
Compensated = "Compensated",
Failed = "Failed"
}
interface SagaStep {
name: string;
action: () => Promise<void>;
compensation: () => Promise<void>;
}
class OrderSaga {
private sagaId: string;
private status: SagaStatus;
private completedSteps: string[] = [];
private currentStep: number = 0;
private steps: SagaStep[] = [
{
name: "CreateOrder",
action: async () => await this.createOrder(),
compensation: async () => await this.cancelOrder()
},
{
name: "ReserveInventory",
action: async () => await this.reserveInventory(),
compensation: async () => await this.releaseInventory()
},
{
name: "ProcessPayment",
action: async () => await this.processPayment(),
compensation: async () => await this.refundPayment()
},
{
name: "ArrangeShipment",
action: async () => await this.arrangeShipment(),
compensation: async () => await this.cancelShipment()
}
];
constructor(
private orderId: string,
private customerId: string,
private items: OrderItem[]
) {
this.sagaId = generateId();
this.status = SagaStatus.Started;
}
async execute(): Promise<void> {
try {
// Execute each step
for (let i = 0; i < this.steps.length; i++) {
this.currentStep = i;
const step = this.steps[i];
console.log(`Executing step: ${step.name}`);
await step.action();
this.completedSteps.push(step.name);
}
this.status = SagaStatus.Completed;
console.log("Saga completed successfully");
} catch (error) {
console.error(`Saga failed at step ${this.currentStep}:`, error);
await this.compensate();
}
}
private async compensate(): Promise<void> {
this.status = SagaStatus.Compensating;
console.log("Starting compensation");
// Compensate in reverse order
for (let i = this.completedSteps.length - 1; i >= 0; i--) {
const stepName = this.completedSteps[i];
const step = this.steps.find(s => s.name === stepName);
if (step) {
try {
console.log(`Compensating step: ${step.name}`);
await step.compensation();
} catch (error) {
console.error(`Compensation failed for ${step.name}:`, error);
// Log for manual intervention
}
}
}
this.status = SagaStatus.Compensated;
console.log("Compensation completed");
}
// Step implementations
private async createOrder(): Promise<void> {
await orderService.create({
orderId: this.orderId,
customerId: this.customerId,
items: this.items
});
}
private async cancelOrder(): Promise<void> {
await orderService.cancel(this.orderId);
}
private async reserveInventory(): Promise<void> {
await inventoryService.reserve(this.orderId, this.items);
}
private async releaseInventory(): Promise<void> {
await inventoryService.release(this.orderId);
}
private async processPayment(): Promise<void> {
const total = this.calculateTotal();
await paymentService.charge(this.customerId, total);
}
private async refundPayment(): Promise<void> {
const total = this.calculateTotal();
await paymentService.refund(this.customerId, total);
}
private async arrangeShipment(): Promise<void> {
await shippingService.createShipment(this.orderId);
}
private async cancelShipment(): Promise<void> {
await shippingService.cancelShipment(this.orderId);
}
private calculateTotal(): Money {
return this.items.reduce(
(sum, item) => sum.add(item.price.multiply(item.quantity)),
new Money(0, "USD")
);
}
}
// Orchestrator service
class SagaOrchestrator {
private activeSagas: Map<string, OrderSaga> = new Map();
async startOrderSaga(
orderId: string,
customerId: string,
items: OrderItem[]
): Promise<void> {
const saga = new OrderSaga(orderId, customerId, items);
this.activeSagas.set(saga.sagaId, saga);
try {
await saga.execute();
} finally {
this.activeSagas.delete(saga.sagaId);
}
}
getSagaStatus(sagaId: string): SagaStatus | null {
const saga = this.activeSagas.get(sagaId);
return saga ? saga.status : null;
}
}Choreography-Based Saga
基于编排的Saga
Services coordinate through events without central orchestrator.
typescript
// Event-driven saga with choreography
class OrderCreatedEvent {
constructor(
public orderId: string,
public customerId: string,
public items: OrderItem[]
) {}
}
class InventoryReservedEvent {
constructor(
public orderId: string,
public reservationId: string
) {}
}
class PaymentProcessedEvent {
constructor(
public orderId: string,
public paymentId: string
) {}
}
class ShipmentArrangedEvent {
constructor(
public orderId: string,
public shipmentId: string
) {}
}
// Compensation events
class InventoryReservationFailedEvent {
constructor(
public orderId: string,
public reason: string
) {}
}
class PaymentFailedEvent {
constructor(
public orderId: string,
public reason: string
) {}
}
// Order service
class OrderService {
constructor(private eventBus: EventBus) {
// Subscribe to compensation events
eventBus.subscribe("InventoryReservationFailed",
this.handleInventoryReservationFailed.bind(this));
eventBus.subscribe("PaymentFailed",
this.handlePaymentFailed.bind(this));
}
async createOrder(order: CreateOrderRequest): Promise<void> {
// Create order in pending state
await this.repository.save({
...order,
status: "Pending"
});
// Publish event to trigger next step
await this.eventBus.publish(
new OrderCreatedEvent(order.orderId, order.customerId, order.items)
);
}
private async handleInventoryReservationFailed(
event: InventoryReservationFailedEvent
): Promise<void> {
await this.repository.updateStatus(event.orderId, "Cancelled");
console.log(`Order ${event.orderId} cancelled: ${event.reason}`);
}
private async handlePaymentFailed(event: PaymentFailedEvent): Promise<void> {
await this.repository.updateStatus(event.orderId, "PaymentFailed");
// Trigger inventory release
await this.eventBus.publish(
new ReleaseInventoryCommand(event.orderId)
);
}
}
// Inventory service
class InventoryService {
constructor(private eventBus: EventBus) {
eventBus.subscribe("OrderCreated",
this.handleOrderCreated.bind(this));
eventBus.subscribe("ReleaseInventory",
this.handleReleaseInventory.bind(this));
}
private async handleOrderCreated(event: OrderCreatedEvent): Promise<void> {
try {
// Reserve inventory
const reservationId = await this.reserveItems(event.items);
// Publish success event
await this.eventBus.publish(
new InventoryReservedEvent(event.orderId, reservationId)
);
} catch (error) {
// Publish failure event
await this.eventBus.publish(
new InventoryReservationFailedEvent(
event.orderId,
error.message
)
);
}
}
private async handleReleaseInventory(
command: ReleaseInventoryCommand
): Promise<void> {
await this.releaseReservation(command.orderId);
}
}
// Payment service
class PaymentService {
constructor(private eventBus: EventBus) {
eventBus.subscribe("InventoryReserved",
this.handleInventoryReserved.bind(this));
eventBus.subscribe("RefundPayment",
this.handleRefundPayment.bind(this));
}
private async handleInventoryReserved(
event: InventoryReservedEvent
): Promise<void> {
try {
// Process payment
const paymentId = await this.chargeCustomer(event.orderId);
// Publish success event
await this.eventBus.publish(
new PaymentProcessedEvent(event.orderId, paymentId)
);
} catch (error) {
// Publish failure event
await this.eventBus.publish(
new PaymentFailedEvent(event.orderId, error.message)
);
}
}
private async handleRefundPayment(
command: RefundPaymentCommand
): Promise<void> {
await this.refund(command.orderId);
}
}服务通过事件协调,无需中央编排器。
typescript
// Event-driven saga with choreography
class OrderCreatedEvent {
constructor(
public orderId: string,
public customerId: string,
public items: OrderItem[]
) {}
}
class InventoryReservedEvent {
constructor(
public orderId: string,
public reservationId: string
) {}
}
class PaymentProcessedEvent {
constructor(
public orderId: string,
public paymentId: string
) {}
}
class ShipmentArrangedEvent {
constructor(
public orderId: string,
public shipmentId: string
) {}
}
// Compensation events
class InventoryReservationFailedEvent {
constructor(
public orderId: string,
public reason: string
) {}
}
class PaymentFailedEvent {
constructor(
public orderId: string,
public reason: string
) {}
}
// Order service
class OrderService {
constructor(private eventBus: EventBus) {
// Subscribe to compensation events
eventBus.subscribe("InventoryReservationFailed",
this.handleInventoryReservationFailed.bind(this));
eventBus.subscribe("PaymentFailed",
this.handlePaymentFailed.bind(this));
}
async createOrder(order: CreateOrderRequest): Promise<void> {
// Create order in pending state
await this.repository.save({
...order,
status: "Pending"
});
// Publish event to trigger next step
await this.eventBus.publish(
new OrderCreatedEvent(order.orderId, order.customerId, order.items)
);
}
private async handleInventoryReservationFailed(
event: InventoryReservationFailedEvent
): Promise<void> {
await this.repository.updateStatus(event.orderId, "Cancelled");
console.log(`Order ${event.orderId} cancelled: ${event.reason}`);
}
private async handlePaymentFailed(event: PaymentFailedEvent): Promise<void> {
await this.repository.updateStatus(event.orderId, "PaymentFailed");
// Trigger inventory release
await this.eventBus.publish(
new ReleaseInventoryCommand(event.orderId)
);
}
}
// Inventory service
class InventoryService {
constructor(private eventBus: EventBus) {
eventBus.subscribe("OrderCreated",
this.handleOrderCreated.bind(this));
eventBus.subscribe("ReleaseInventory",
this.handleReleaseInventory.bind(this));
}
private async handleOrderCreated(event: OrderCreatedEvent): Promise<void> {
try {
// Reserve inventory
const reservationId = await this.reserveItems(event.items);
// Publish success event
await this.eventBus.publish(
new InventoryReservedEvent(event.orderId, reservationId)
);
} catch (error) {
// Publish failure event
await this.eventBus.publish(
new InventoryReservationFailedEvent(
event.orderId,
error.message
)
);
}
}
private async handleReleaseInventory(
command: ReleaseInventoryCommand
): Promise<void> {
await this.releaseReservation(command.orderId);
}
}
// Payment service
class PaymentService {
constructor(private eventBus: EventBus) {
eventBus.subscribe("InventoryReserved",
this.handleInventoryReserved.bind(this));
eventBus.subscribe("RefundPayment",
this.handleRefundPayment.bind(this));
}
private async handleInventoryReserved(
event: InventoryReservedEvent
): Promise<void> {
try {
// Process payment
const paymentId = await this.chargeCustomer(event.orderId);
// Publish success event
await this.eventBus.publish(
new PaymentProcessedEvent(event.orderId, paymentId)
);
} catch (error) {
// Publish failure event
await this.eventBus.publish(
new PaymentFailedEvent(event.orderId, error.message)
);
}
}
private async handleRefundPayment(
command: RefundPaymentCommand
): Promise<void> {
await this.refund(command.orderId);
}
}API Gateway Pattern
API网关模式
Single entry point for clients, routing requests to appropriate microservices and handling cross-cutting concerns.
API网关是客户端的单一入口点,负责将请求路由到相应的微服务,并处理横切关注点。
Implementation
实现
typescript
class APIGateway {
constructor(
private router: Router,
private authService: AuthService,
private rateLimiter: RateLimiter,
private circuitBreaker: CircuitBreaker,
private loadBalancer: LoadBalancer
) {
this.setupRoutes();
}
private setupRoutes(): void {
// User service routes
this.router.get("/api/users/:id",
this.authenticate.bind(this),
this.rateLimit.bind(this),
this.getUserHandler.bind(this)
);
// Order service routes
this.router.post("/api/orders",
this.authenticate.bind(this),
this.rateLimit.bind(this),
this.createOrderHandler.bind(this)
);
// Product service routes
this.router.get("/api/products",
this.rateLimit.bind(this),
this.getProductsHandler.bind(this)
);
}
// Middleware: Authentication
private async authenticate(
req: Request,
res: Response,
next: NextFunction
): Promise<void> {
try {
const token = req.headers.authorization?.replace("Bearer ", "");
if (!token) {
res.status(401).json({ error: "Unauthorized" });
return;
}
const user = await this.authService.validateToken(token);
req.user = user;
next();
} catch (error) {
res.status(401).json({ error: "Invalid token" });
}
}
// Middleware: Rate limiting
private async rateLimit(
req: Request,
res: Response,
next: NextFunction
): Promise<void> {
const clientId = req.ip || req.user?.id;
if (await this.rateLimiter.isAllowed(clientId)) {
next();
} else {
res.status(429).json({ error: "Too many requests" });
}
}
// Handler: Get user
private async getUserHandler(req: Request, res: Response): Promise<void> {
try {
const userId = req.params.id;
// Call user service with circuit breaker
const user = await this.circuitBreaker.execute(
"user-service",
async () => {
const instance = this.loadBalancer.getInstance("user-service");
return await instance.getUser(userId);
}
);
res.json(user);
} catch (error) {
res.status(500).json({ error: "Internal server error" });
}
}
// Handler: Create order
private async createOrderHandler(
req: Request,
res: Response
): Promise<void> {
try {
// Aggregate data from multiple services
const [user, products, inventory] = await Promise.all([
this.callUserService(req.user.id),
this.callProductService(req.body.items),
this.callInventoryService(req.body.items)
]);
// Call order service
const order = await this.callOrderService({
user,
items: req.body.items,
inventory
});
res.status(201).json(order);
} catch (error) {
res.status(500).json({ error: "Internal server error" });
}
}
private async callUserService(userId: string): Promise<User> {
return await this.circuitBreaker.execute("user-service", async () => {
const instance = this.loadBalancer.getInstance("user-service");
return await instance.getUser(userId);
});
}
private async callProductService(items: any[]): Promise<Product[]> {
return await this.circuitBreaker.execute("product-service", async () => {
const instance = this.loadBalancer.getInstance("product-service");
return await instance.getProducts(items.map(i => i.productId));
});
}
}
// Rate limiter implementation
class RateLimiter {
private requests: Map<string, number[]> = new Map();
constructor(
private maxRequests: number = 100,
private windowMs: number = 60000 // 1 minute
) {}
async isAllowed(clientId: string): Promise<boolean> {
const now = Date.now();
const windowStart = now - this.windowMs;
// Get existing requests
const clientRequests = this.requests.get(clientId) || [];
// Filter out old requests
const recentRequests = clientRequests.filter(t => t > windowStart);
// Check if under limit
if (recentRequests.length < this.maxRequests) {
recentRequests.push(now);
this.requests.set(clientId, recentRequests);
return true;
}
return false;
}
}
// Load balancer
class LoadBalancer {
private services: Map<string, ServiceInstance[]> = new Map();
private currentIndex: Map<string, number> = new Map();
registerService(name: string, instance: ServiceInstance): void {
if (!this.services.has(name)) {
this.services.set(name, []);
this.currentIndex.set(name, 0);
}
this.services.get(name)!.push(instance);
}
getInstance(serviceName: string): ServiceInstance {
const instances = this.services.get(serviceName);
if (!instances || instances.length === 0) {
throw new Error(`No instances available for ${serviceName}`);
}
// Round-robin selection
const index = this.currentIndex.get(serviceName)!;
const instance = instances[index];
// Update index for next call
this.currentIndex.set(
serviceName,
(index + 1) % instances.length
);
return instance;
}
}typescript
class APIGateway {
constructor(
private router: Router,
private authService: AuthService,
private rateLimiter: RateLimiter,
private circuitBreaker: CircuitBreaker,
private loadBalancer: LoadBalancer
) {
this.setupRoutes();
}
private setupRoutes(): void {
// User service routes
this.router.get("/api/users/:id",
this.authenticate.bind(this),
this.rateLimit.bind(this),
this.getUserHandler.bind(this)
);
// Order service routes
this.router.post("/api/orders",
this.authenticate.bind(this),
this.rateLimit.bind(this),
this.createOrderHandler.bind(this)
);
// Product service routes
this.router.get("/api/products",
this.rateLimit.bind(this),
this.getProductsHandler.bind(this)
);
}
// Middleware: Authentication
private async authenticate(
req: Request,
res: Response,
next: NextFunction
): Promise<void> {
try {
const token = req.headers.authorization?.replace("Bearer ", "");
if (!token) {
res.status(401).json({ error: "Unauthorized" });
return;
}
const user = await this.authService.validateToken(token);
req.user = user;
next();
} catch (error) {
res.status(401).json({ error: "Invalid token" });
}
}
// Middleware: Rate limiting
private async rateLimit(
req: Request,
res: Response,
next: NextFunction
): Promise<void> {
const clientId = req.ip || req.user?.id;
if (await this.rateLimiter.isAllowed(clientId)) {
next();
} else {
res.status(429).json({ error: "Too many requests" });
}
}
// Handler: Get user
private async getUserHandler(req: Request, res: Response): Promise<void> {
try {
const userId = req.params.id;
// Call user service with circuit breaker
const user = await this.circuitBreaker.execute(
"user-service",
async () => {
const instance = this.loadBalancer.getInstance("user-service");
return await instance.getUser(userId);
}
);
res.json(user);
} catch (error) {
res.status(500).json({ error: "Internal server error" });
}
}
// Handler: Create order
private async createOrderHandler(
req: Request,
res: Response
): Promise<void> {
try {
// Aggregate data from multiple services
const [user, products, inventory] = await Promise.all([
this.callUserService(req.user.id),
this.callProductService(req.body.items),
this.callInventoryService(req.body.items)
]);
// Call order service
const order = await this.callOrderService({
user,
items: req.body.items,
inventory
});
res.status(201).json(order);
} catch (error) {
res.status(500).json({ error: "Internal server error" });
}
}
private async callUserService(userId: string): Promise<User> {
return await this.circuitBreaker.execute("user-service", async () => {
const instance = this.loadBalancer.getInstance("user-service");
return await instance.getUser(userId);
});
}
private async callProductService(items: any[]): Promise<Product[]> {
return await this.circuitBreaker.execute("product-service", async () => {
const instance = this.loadBalancer.getInstance("product-service");
return await instance.getProducts(items.map(i => i.productId));
});
}
}
// Rate limiter implementation
class RateLimiter {
private requests: Map<string, number[]> = new Map();
constructor(
private maxRequests: number = 100,
private windowMs: number = 60000 // 1 minute
) {}
async isAllowed(clientId: string): Promise<boolean> {
const now = Date.now();
const windowStart = now - this.windowMs;
// Get existing requests
const clientRequests = this.requests.get(clientId) || [];
// Filter out old requests
const recentRequests = clientRequests.filter(t => t > windowStart);
// Check if under limit
if (recentRequests.length < this.maxRequests) {
recentRequests.push(now);
this.requests.set(clientId, recentRequests);
return true;
}
return false;
}
}
// Load balancer
class LoadBalancer {
private services: Map<string, ServiceInstance[]> = new Map();
private currentIndex: Map<string, number> = new Map();
registerService(name: string, instance: ServiceInstance): void {
if (!this.services.has(name)) {
this.services.set(name, []);
this.currentIndex.set(name, 0);
}
this.services.get(name)!.push(instance);
}
getInstance(serviceName: string): ServiceInstance {
const instances = this.services.get(serviceName);
if (!instances || instances.length === 0) {
throw new Error(`No instances available for ${serviceName}`);
}
// Round-robin selection
const index = this.currentIndex.get(serviceName)!;
const instance = instances[index];
// Update index for next call
this.currentIndex.set(
serviceName,
(index + 1) % instances.length
);
return instance;
}
}Backend for Frontend (BFF) Pattern
Backend for Frontend(BFF)模式
typescript
// Separate BFFs for different clients
class WebBFF {
constructor(
private userService: UserService,
private productService: ProductService,
private orderService: OrderService
) {}
// Optimized for web client needs
async getHomePage(userId: string): Promise<WebHomePageData> {
const [user, recommendations, recentOrders] = await Promise.all([
this.userService.getUser(userId),
this.productService.getRecommendations(userId, 10),
this.orderService.getRecentOrders(userId, 5)
]);
return {
user: {
name: user.name,
email: user.email,
avatar: user.avatarUrl
},
recommendations: recommendations.map(p => ({
id: p.id,
name: p.name,
price: p.price,
imageUrl: p.images[0], // Full images for web
rating: p.averageRating
})),
recentOrders: recentOrders.map(o => ({
orderId: o.id,
date: o.createdAt,
total: o.totalAmount,
status: o.status,
itemCount: o.items.length
}))
};
}
}
class MobileBFF {
constructor(
private userService: UserService,
private productService: ProductService,
private orderService: OrderService
) {}
// Optimized for mobile client needs
async getHomePage(userId: string): Promise<MobileHomePageData> {
const [user, recommendations, recentOrders] = await Promise.all([
this.userService.getUser(userId),
this.productService.getRecommendations(userId, 5), // Fewer items
this.orderService.getRecentOrders(userId, 3)
]);
return {
user: {
name: user.name,
avatar: user.avatarThumbnailUrl // Smaller images for mobile
},
recommendations: recommendations.map(p => ({
id: p.id,
name: p.name,
price: p.price,
thumbnail: p.thumbnails.small, // Optimized image size
rating: Math.round(p.averageRating) // Simplified rating
})),
recentOrders: recentOrders.map(o => ({
id: o.id,
date: o.createdAt.toISOString(),
total: o.totalAmount,
status: o.status
}))
};
}
}typescript
// Separate BFFs for different clients
class WebBFF {
constructor(
private userService: UserService,
private productService: ProductService,
private orderService: OrderService
) {}
// Optimized for web client needs
async getHomePage(userId: string): Promise<WebHomePageData> {
const [user, recommendations, recentOrders] = await Promise.all([
this.userService.getUser(userId),
this.productService.getRecommendations(userId, 10),
this.orderService.getRecentOrders(userId, 5)
]);
return {
user: {
name: user.name,
email: user.email,
avatar: user.avatarUrl
},
recommendations: recommendations.map(p => ({
id: p.id,
name: p.name,
price: p.price,
imageUrl: p.images[0], // Full images for web
rating: p.averageRating
})),
recentOrders: recentOrders.map(o => ({
orderId: o.id,
date: o.createdAt,
total: o.totalAmount,
status: o.status,
itemCount: o.items.length
}))
};
}
}
class MobileBFF {
constructor(
private userService: UserService,
private productService: ProductService,
private orderService: OrderService
) {}
// Optimized for mobile client needs
async getHomePage(userId: string): Promise<MobileHomePageData> {
const [user, recommendations, recentOrders] = await Promise.all([
this.userService.getUser(userId),
this.productService.getRecommendations(userId, 5), // Fewer items
this.orderService.getRecentOrders(userId, 3)
]);
return {
user: {
name: user.name,
avatar: user.avatarThumbnailUrl // Smaller images for mobile
},
recommendations: recommendations.map(p => ({
id: p.id,
name: p.name,
price: p.price,
thumbnail: p.thumbnails.small, // Optimized image size
rating: Math.round(p.averageRating) // Simplified rating
})),
recentOrders: recentOrders.map(o => ({
id: o.id,
date: o.createdAt.toISOString(),
total: o.totalAmount,
status: o.status
}))
};
}
}Service Mesh Pattern
服务网格模式
Infrastructure layer for service-to-service communication providing observability, traffic management, and security.
服务网格是服务间通信的基础设施层,提供可观察性、流量管理和安全能力。
Key Features
核心特性
typescript
// Service mesh configuration example (Istio)
const serviceMeshConfig = {
// Traffic management
virtualService: {
name: "product-service",
hosts: ["product-service"],
http: [
{
match: [{ uri: { prefix: "/api/v1" } }],
route: [
{
destination: {
host: "product-service",
subset: "v1"
},
weight: 90
},
{
destination: {
host: "product-service",
subset: "v2"
},
weight: 10 // Canary deployment
}
],
retries: {
attempts: 3,
perTryTimeout: "2s"
},
timeout: "10s"
}
]
},
// Destination rules
destinationRule: {
name: "product-service",
host: "product-service",
trafficPolicy: {
connectionPool: {
tcp: {
maxConnections: 100
},
http: {
http1MaxPendingRequests: 50,
http2MaxRequests: 100,
maxRequestsPerConnection: 2
}
},
loadBalancer: {
simple: "ROUND_ROBIN"
},
outlierDetection: {
consecutive5xxErrors: 5,
interval: "30s",
baseEjectionTime: "30s",
maxEjectionPercent: 50
}
},
subsets: [
{
name: "v1",
labels: { version: "v1" }
},
{
name: "v2",
labels: { version: "v2" }
}
]
},
// Circuit breaker
circuitBreaker: {
consecutiveErrors: 5,
interval: "30s",
baseEjectionTime: "30s",
maxEjectionPercent: 50
}
};typescript
// Service mesh configuration example (Istio)
const serviceMeshConfig = {
// Traffic management
virtualService: {
name: "product-service",
hosts: ["product-service"],
http: [
{
match: [{ uri: { prefix: "/api/v1" } }],
route: [
{
destination: {
host: "product-service",
subset: "v1"
},
weight: 90
},
{
destination: {
host: "product-service",
subset: "v2"
},
weight: 10 // Canary deployment
}
],
retries: {
attempts: 3,
perTryTimeout: "2s"
},
timeout: "10s"
}
]
},
// Destination rules
destinationRule: {
name: "product-service",
host: "product-service",
trafficPolicy: {
connectionPool: {
tcp: {
maxConnections: 100
},
http: {
http1MaxPendingRequests: 50,
http2MaxRequests: 100,
maxRequestsPerConnection: 2
}
},
loadBalancer: {
simple: "ROUND_ROBIN"
},
outlierDetection: {
consecutive5xxErrors: 5,
interval: "30s",
baseEjectionTime: "30s",
maxEjectionPercent: 50
}
},
subsets: [
{
name: "v1",
labels: { version: "v1" }
},
{
name: "v2",
labels: { version: "v2" }
}
]
},
// Circuit breaker
circuitBreaker: {
consecutiveErrors: 5,
interval: "30s",
baseEjectionTime: "30s",
maxEjectionPercent: 50
}
};Resilience Patterns
弹性模式
Circuit Breaker
断路器
Prevent cascading failures by stopping requests to failing services.
typescript
enum CircuitState {
Closed = "Closed", // Normal operation
Open = "Open", // Blocking requests
HalfOpen = "HalfOpen" // Testing if service recovered
}
class CircuitBreaker {
private state: CircuitState = CircuitState.Closed;
private failureCount: number = 0;
private successCount: number = 0;
private lastFailureTime: number = 0;
constructor(
private failureThreshold: number = 5,
private successThreshold: number = 2,
private timeout: number = 60000 // 1 minute
) {}
async execute<T>(operation: () => Promise<T>): Promise<T> {
if (this.state === CircuitState.Open) {
if (Date.now() - this.lastFailureTime > this.timeout) {
this.state = CircuitState.HalfOpen;
this.successCount = 0;
} else {
throw new Error("Circuit breaker is OPEN");
}
}
try {
const result = await operation();
this.onSuccess();
return result;
} catch (error) {
this.onFailure();
throw error;
}
}
private onSuccess(): void {
this.failureCount = 0;
if (this.state === CircuitState.HalfOpen) {
this.successCount++;
if (this.successCount >= this.successThreshold) {
this.state = CircuitState.Closed;
this.successCount = 0;
}
}
}
private onFailure(): void {
this.failureCount++;
this.lastFailureTime = Date.now();
if (this.failureCount >= this.failureThreshold) {
this.state = CircuitState.Open;
}
}
getState(): CircuitState {
return this.state;
}
}通过停止向故障服务发送请求,防止级联故障。
typescript
enum CircuitState {
Closed = "Closed", // Normal operation
Open = "Open", // Blocking requests
HalfOpen = "HalfOpen" // Testing if service recovered
}
class CircuitBreaker {
private state: CircuitState = CircuitState.Closed;
private failureCount: number = 0;
private successCount: number = 0;
private lastFailureTime: number = 0;
constructor(
private failureThreshold: number = 5,
private successThreshold: number = 2,
private timeout: number = 60000 // 1 minute
) {}
async execute<T>(operation: () => Promise<T>): Promise<T> {
if (this.state === CircuitState.Open) {
if (Date.now() - this.lastFailureTime > this.timeout) {
this.state = CircuitState.HalfOpen;
this.successCount = 0;
} else {
throw new Error("Circuit breaker is OPEN");
}
}
try {
const result = await operation();
this.onSuccess();
return result;
} catch (error) {
this.onFailure();
throw error;
}
}
private onSuccess(): void {
this.failureCount = 0;
if (this.state === CircuitState.HalfOpen) {
this.successCount++;
if (this.successCount >= this.successThreshold) {
this.state = CircuitState.Closed;
this.successCount = 0;
}
}
}
private onFailure(): void {
this.failureCount++;
this.lastFailureTime = Date.now();
if (this.failureCount >= this.failureThreshold) {
this.state = CircuitState.Open;
}
}
getState(): CircuitState {
return this.state;
}
}Retry Pattern
重试模式
Automatically retry failed operations with exponential backoff.
typescript
class RetryPolicy {
constructor(
private maxRetries: number = 3,
private initialDelayMs: number = 100,
private maxDelayMs: number = 5000,
private backoffMultiplier: number = 2
) {}
async execute<T>(
operation: () => Promise<T>,
isRetryable: (error: Error) => boolean = () => true
): Promise<T> {
let lastError: Error;
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
try {
return await operation();
} catch (error) {
lastError = error as Error;
if (attempt === this.maxRetries || !isRetryable(lastError)) {
throw lastError;
}
const delay = this.calculateDelay(attempt);
await this.sleep(delay);
}
}
throw lastError!;
}
private calculateDelay(attempt: number): number {
const delay = this.initialDelayMs * Math.pow(this.backoffMultiplier, attempt);
return Math.min(delay, this.maxDelayMs);
}
private sleep(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}
}
// Usage
const retryPolicy = new RetryPolicy(3, 100, 5000, 2);
await retryPolicy.execute(
async () => await apiClient.get("/users/123"),
(error) => error.statusCode >= 500 // Only retry server errors
);使用指数退避自动重试失败的操作。
typescript
class RetryPolicy {
constructor(
private maxRetries: number = 3,
private initialDelayMs: number = 100,
private maxDelayMs: number = 5000,
private backoffMultiplier: number = 2
) {}
async execute<T>(
operation: () => Promise<T>,
isRetryable: (error: Error) => boolean = () => true
): Promise<T> {
let lastError: Error;
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
try {
return await operation();
} catch (error) {
lastError = error as Error;
if (attempt === this.maxRetries || !isRetryable(lastError)) {
throw lastError;
}
const delay = this.calculateDelay(attempt);
await this.sleep(delay);
}
}
throw lastError!;
}
private calculateDelay(attempt: number): number {
const delay = this.initialDelayMs * Math.pow(this.backoffMultiplier, attempt);
return Math.min(delay, this.maxDelayMs);
}
private sleep(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}
}
// Usage
const retryPolicy = new RetryPolicy(3, 100, 5000, 2);
await retryPolicy.execute(
async () => await apiClient.get("/users/123"),
(error) => error.statusCode >= 500 // Only retry server errors
);Bulkhead Pattern
舱壁模式
Isolate resources to prevent failures from affecting entire system.
typescript
class Bulkhead {
private activeRequests: number = 0;
private queue: Array<{
resolve: (value: any) => void;
reject: (error: any) => void;
operation: () => Promise<any>;
}> = [];
constructor(
private maxConcurrent: number = 10,
private maxQueueSize: number = 100
) {}
async execute<T>(operation: () => Promise<T>): Promise<T> {
if (this.activeRequests < this.maxConcurrent) {
return await this.executeOperation(operation);
}
if (this.queue.length >= this.maxQueueSize) {
throw new Error("Bulkhead queue full");
}
return new Promise((resolve, reject) => {
this.queue.push({ resolve, reject, operation });
});
}
private async executeOperation<T>(
operation: () => Promise<T>
): Promise<T> {
this.activeRequests++;
try {
const result = await operation();
this.processQueue();
return result;
} catch (error) {
this.processQueue();
throw error;
} finally {
this.activeRequests--;
}
}
private processQueue(): void {
if (this.queue.length > 0 &&
this.activeRequests < this.maxConcurrent) {
const { resolve, reject, operation } = this.queue.shift()!;
this.executeOperation(operation)
.then(resolve)
.catch(reject);
}
}
}隔离资源,防止故障影响整个系统。
typescript
class Bulkhead {
private activeRequests: number = 0;
private queue: Array<{
resolve: (value: any) => void;
reject: (error: any) => void;
operation: () => Promise<any>;
}> = [];
constructor(
private maxConcurrent: number = 10,
private maxQueueSize: number = 100
) {}
async execute<T>(operation: () => Promise<T>): Promise<T> {
if (this.activeRequests < this.maxConcurrent) {
return await this.executeOperation(operation);
}
if (this.queue.length >= this.maxQueueSize) {
throw new Error("Bulkhead queue full");
}
return new Promise((resolve, reject) => {
this.queue.push({ resolve, reject, operation });
});
}
private async executeOperation<T>(
operation: () => Promise<T>
): Promise<T> {
this.activeRequests++;
try {
const result = await operation();
this.processQueue();
return result;
} catch (error) {
this.processQueue();
throw error;
} finally {
this.activeRequests--;
}
}
private processQueue(): void {
if (this.queue.length > 0 &&
this.activeRequests < this.maxConcurrent) {
const { resolve, reject, operation } = this.queue.shift()!;
this.executeOperation(operation)
.then(resolve)
.catch(reject);
}
}
}Timeout Pattern
超时模式
Prevent indefinite waits by setting time limits.
typescript
class TimeoutPolicy {
constructor(private timeoutMs: number = 30000) {}
async execute<T>(operation: () => Promise<T>): Promise<T> {
return Promise.race([
operation(),
this.timeout()
]);
}
private timeout(): Promise<never> {
return new Promise((_, reject) => {
setTimeout(() => {
reject(new Error(`Operation timed out after ${this.timeoutMs}ms`));
}, this.timeoutMs);
});
}
}通过设置时间限制,防止无限期等待。
typescript
class TimeoutPolicy {
constructor(private timeoutMs: number = 30000) {}
async execute<T>(operation: () => Promise<T>): Promise<T> {
return Promise.race([
operation(),
this.timeout()
]);
}
private timeout(): Promise<never> {
return new Promise((_, reject) => {
setTimeout(() => {
reject(new Error(`Operation timed out after ${this.timeoutMs}ms`));
}, this.timeoutMs);
});
}
}Fallback Pattern
降级模式
Provide alternative response when operation fails.
typescript
class FallbackPolicy<T> {
constructor(private fallbackFn: () => Promise<T>) {}
async execute(operation: () => Promise<T>): Promise<T> {
try {
return await operation();
} catch (error) {
console.warn("Operation failed, using fallback:", error);
return await this.fallbackFn();
}
}
}
// Usage
const getUserWithFallback = new FallbackPolicy(async () => ({
id: "default",
name: "Guest User",
email: "guest@example.com"
}));
const user = await getUserWithFallback.execute(
async () => await userService.getUser(userId)
);当操作失败时,提供替代响应。
typescript
class FallbackPolicy<T> {
constructor(private fallbackFn: () => Promise<T>) {}
async execute(operation: () => Promise<T>): Promise<T> {
try {
return await operation();
} catch (error) {
console.warn("Operation failed, using fallback:", error);
return await this.fallbackFn();
}
}
}
// Usage
const getUserWithFallback = new FallbackPolicy(async () => ({
id: "default",
name: "Guest User",
email: "guest@example.com"
}));
const user = await getUserWithFallback.execute(
async () => await userService.getUser(userId)
);Combined Resilience Strategy
组合弹性策略
typescript
class ResilientClient {
private circuitBreaker: CircuitBreaker;
private retryPolicy: RetryPolicy;
private timeoutPolicy: TimeoutPolicy;
private fallbackPolicy: FallbackPolicy<any>;
private bulkhead: Bulkhead;
constructor() {
this.circuitBreaker = new CircuitBreaker(5, 2, 60000);
this.retryPolicy = new RetryPolicy(3, 100, 5000, 2);
this.timeoutPolicy = new TimeoutPolicy(10000);
this.fallbackPolicy = new FallbackPolicy(async () => null);
this.bulkhead = new Bulkhead(10, 100);
}
async call<T>(
operation: () => Promise<T>,
options?: {
timeout?: number;
retries?: number;
fallback?: () => Promise<T>;
}
): Promise<T> {
const timeoutPolicy = options?.timeout
? new TimeoutPolicy(options.timeout)
: this.timeoutPolicy;
const fallbackPolicy = options?.fallback
? new FallbackPolicy(options.fallback)
: this.fallbackPolicy;
return await fallbackPolicy.execute(async () => {
return await this.bulkhead.execute(async () => {
return await this.circuitBreaker.execute(async () => {
return await this.retryPolicy.execute(async () => {
return await timeoutPolicy.execute(operation);
});
});
});
});
}
}typescript
class ResilientClient {
private circuitBreaker: CircuitBreaker;
private retryPolicy: RetryPolicy;
private timeoutPolicy: TimeoutPolicy;
private fallbackPolicy: FallbackPolicy<any>;
private bulkhead: Bulkhead;
constructor() {
this.circuitBreaker = new CircuitBreaker(5, 2, 60000);
this.retryPolicy = new RetryPolicy(3, 100, 5000, 2);
this.timeoutPolicy = new TimeoutPolicy(10000);
this.fallbackPolicy = new FallbackPolicy(async () => null);
this.bulkhead = new Bulkhead(10, 100);
}
async call<T>(
operation: () => Promise<T>,
options?: {
timeout?: number;
retries?: number;
fallback?: () => Promise<T>;
}
): Promise<T> {
const timeoutPolicy = options?.timeout
? new TimeoutPolicy(options.timeout)
: this.timeoutPolicy;
const fallbackPolicy = options?.fallback
? new FallbackPolicy(options.fallback)
: this.fallbackPolicy;
return await fallbackPolicy.execute(async () => {
return await this.bulkhead.execute(async () => {
return await this.circuitBreaker.execute(async () => {
return await this.retryPolicy.execute(async () => {
return await timeoutPolicy.execute(operation);
});
});
});
});
}
}Scalability Patterns
可扩展性模式
Horizontal Scaling (Scale Out)
水平扩展(向外扩展)
Add more instances to handle increased load.
typescript
// Load balancer for horizontal scaling
class RoundRobinLoadBalancer {
private instances: string[] = [];
private currentIndex: number = 0;
addInstance(url: string): void {
this.instances.push(url);
}
removeInstance(url: string): void {
this.instances = this.instances.filter(i => i !== url);
}
getNextInstance(): string {
if (this.instances.length === 0) {
throw new Error("No instances available");
}
const instance = this.instances[this.currentIndex];
this.currentIndex = (this.currentIndex + 1) % this.instances.length;
return instance;
}
}
// Auto-scaling based on metrics
class AutoScaler {
constructor(
private minInstances: number = 2,
private maxInstances: number = 10,
private targetCPU: number = 70
) {}
async scale(currentInstances: number, currentCPU: number): Promise<number> {
if (currentCPU > this.targetCPU) {
// Scale up
const desiredInstances = Math.ceil(
currentInstances * (currentCPU / this.targetCPU)
);
return Math.min(desiredInstances, this.maxInstances);
} else if (currentCPU < this.targetCPU * 0.5) {
// Scale down
const desiredInstances = Math.floor(
currentInstances * (currentCPU / this.targetCPU)
);
return Math.max(desiredInstances, this.minInstances);
}
return currentInstances;
}
}添加更多实例以处理增加的负载。
typescript
// Load balancer for horizontal scaling
class RoundRobinLoadBalancer {
private instances: string[] = [];
private currentIndex: number = 0;
addInstance(url: string): void {
this.instances.push(url);
}
removeInstance(url: string): void {
this.instances = this.instances.filter(i => i !== url);
}
getNextInstance(): string {
if (this.instances.length === 0) {
throw new Error("No instances available");
}
const instance = this.instances[this.currentIndex];
this.currentIndex = (this.currentIndex + 1) % this.instances.length;
return instance;
}
}
// Auto-scaling based on metrics
class AutoScaler {
constructor(
private minInstances: number = 2,
private maxInstances: number = 10,
private targetCPU: number = 70
) {}
async scale(currentInstances: number, currentCPU: number): Promise<number> {
if (currentCPU > this.targetCPU) {
// Scale up
const desiredInstances = Math.ceil(
currentInstances * (currentCPU / this.targetCPU)
);
return Math.min(desiredInstances, this.maxInstances);
} else if (currentCPU < this.targetCPU * 0.5) {
// Scale down
const desiredInstances = Math.floor(
currentInstances * (currentCPU / this.targetCPU)
);
return Math.max(desiredInstances, this.minInstances);
}
return currentInstances;
}
}Caching Strategies
缓存策略
typescript
// Cache-aside pattern
class CacheAsideRepository {
constructor(
private cache: Cache,
private database: Database,
private ttl: number = 3600
) {}
async get(id: string): Promise<any> {
// Try cache first
const cached = await this.cache.get(id);
if (cached) {
return cached;
}
// Cache miss - get from database
const data = await this.database.findById(id);
if (data) {
await this.cache.set(id, data, this.ttl);
}
return data;
}
async update(id: string, data: any): Promise<void> {
// Update database
await this.database.update(id, data);
// Invalidate cache
await this.cache.delete(id);
}
}
// Write-through cache
class WriteThroughCache {
constructor(
private cache: Cache,
private database: Database
) {}
async write(id: string, data: any): Promise<void> {
// Write to both cache and database
await Promise.all([
this.cache.set(id, data),
this.database.save(id, data)
]);
}
}
// Write-behind cache
class WriteBehindCache {
private writeQueue: Map<string, any> = new Map();
constructor(
private cache: Cache,
private database: Database,
private flushInterval: number = 5000
) {
this.startFlushInterval();
}
async write(id: string, data: any): Promise<void> {
// Write to cache immediately
await this.cache.set(id, data);
// Queue for database write
this.writeQueue.set(id, data);
}
private startFlushInterval(): void {
setInterval(async () => {
await this.flush();
}, this.flushInterval);
}
private async flush(): Promise<void> {
const entries = Array.from(this.writeQueue.entries());
this.writeQueue.clear();
await Promise.all(
entries.map(([id, data]) => this.database.save(id, data))
);
}
}typescript
// Cache-aside pattern
class CacheAsideRepository {
constructor(
private cache: Cache,
private database: Database,
private ttl: number = 3600
) {}
async get(id: string): Promise<any> {
// Try cache first
const cached = await this.cache.get(id);
if (cached) {
return cached;
}
// Cache miss - get from database
const data = await this.database.findById(id);
if (data) {
await this.cache.set(id, data, this.ttl);
}
return data;
}
async update(id: string, data: any): Promise<void> {
// Update database
await this.database.update(id, data);
// Invalidate cache
await this.cache.delete(id);
}
}
// Write-through cache
class WriteThroughCache {
constructor(
private cache: Cache,
private database: Database
) {}
async write(id: string, data: any): Promise<void> {
// Write to both cache and database
await Promise.all([
this.cache.set(id, data),
this.database.save(id, data)
]);
}
}
// Write-behind cache
class WriteBehindCache {
private writeQueue: Map<string, any> = new Map();
constructor(
private cache: Cache,
private database: Database,
private flushInterval: number = 5000
) {
this.startFlushInterval();
}
async write(id: string, data: any): Promise<void> {
// Write to cache immediately
await this.cache.set(id, data);
// Queue for database write
this.writeQueue.set(id, data);
}
private startFlushInterval(): void {
setInterval(async () => {
await this.flush();
}, this.flushInterval);
}
private async flush(): Promise<void> {
const entries = Array.from(this.writeQueue.entries());
this.writeQueue.clear();
await Promise.all(
entries.map(([id, data]) => this.database.save(id, data))
);
}
}Database Sharding
数据库分片
typescript
// Shard key-based routing
class ShardRouter {
private shards: Map<number, Database> = new Map();
private totalShards: number;
constructor(shards: Database[]) {
this.totalShards = shards.length;
shards.forEach((shard, index) => {
this.shards.set(index, shard);
});
}
private getShardIndex(key: string): number {
// Hash-based sharding
const hash = this.hashCode(key);
return Math.abs(hash) % this.totalShards;
}
private hashCode(str: string): number {
let hash = 0;
for (let i = 0; i < str.length; i++) {
const char = str.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
hash = hash & hash; // Convert to 32-bit integer
}
return hash;
}
getShard(key: string): Database {
const index = this.getShardIndex(key);
return this.shards.get(index)!;
}
async save(key: string, data: any): Promise<void> {
const shard = this.getShard(key);
await shard.save(key, data);
}
async find(key: string): Promise<any> {
const shard = this.getShard(key);
return await shard.findById(key);
}
}typescript
// Shard key-based routing
class ShardRouter {
private shards: Map<number, Database> = new Map();
private totalShards: number;
constructor(shards: Database[]) {
this.totalShards = shards.length;
shards.forEach((shard, index) => {
this.shards.set(index, shard);
});
}
private getShardIndex(key: string): number {
// Hash-based sharding
const hash = this.hashCode(key);
return Math.abs(hash) % this.totalShards;
}
private hashCode(str: string): number {
let hash = 0;
for (let i = 0; i < str.length; i++) {
const char = str.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
hash = hash & hash; // Convert to 32-bit integer
}
return hash;
}
getShard(key: string): Database {
const index = this.getShardIndex(key);
return this.shards.get(index)!;
}
async save(key: string, data: any): Promise<void> {
const shard = this.getShard(key);
await shard.save(key, data);
}
async find(key: string): Promise<any> {
const shard = this.getShard(key);
return await shard.findById(key);
}
}Best Practices
最佳实践
Architecture Design
架构设计
- Start with the domain: Understand business requirements before choosing patterns
- Keep it simple: Don't over-engineer; add complexity only when needed
- Design for failure: Assume services will fail; build resilience
- Loose coupling: Services should be independent and deployable separately
- High cohesion: Group related functionality together
- API-first design: Define contracts before implementation
- Versioning strategy: Plan for API evolution from the start
- Observability: Build in logging, metrics, and tracing from day one
- 从领域开始:在选择模式前,先理解业务需求
- 保持简单:不要过度设计,仅在需要时增加复杂度
- 为故障设计:假设服务会故障,构建弹性系统
- 松耦合:服务应独立且可单独部署
- 高内聚:将相关功能分组
- API优先设计:在实现前定义契约
- 版本化策略:从一开始就规划API演进
- 可观察性:从第一天就内置日志、指标和追踪能力
Domain-Driven Design
领域驱动设计
- Collaborate with domain experts: Build shared understanding
- Use ubiquitous language: Consistent terminology everywhere
- Model bounded contexts: Clear boundaries prevent model confusion
- Small aggregates: Better performance and clearer boundaries
- Reference by ID: Aggregates reference others by identity
- Protect invariants: Aggregate roots enforce all business rules
- Domain events: Capture important business occurrences
- Repository per aggregate: One repository per aggregate root
- 与领域专家协作:建立共同理解
- 使用通用语言:在所有地方使用一致的术语
- 建模限界上下文:清晰的边界避免模型混淆
- 小型聚合:性能更好,边界更清晰
- 通过ID引用:聚合仅通过ID引用其他聚合
- 保护不变量:聚合根强制执行所有业务规则
- 领域事件:捕获重要的业务事件
- 每个聚合对应一个仓库:每个聚合根对应一个仓库
Event Sourcing & CQRS
事件溯源与CQRS
- Event naming: Use past tense, business-meaningful names
- Event immutability: Never modify published events
- Event versioning: Plan for event schema evolution
- Snapshots: Use for long event streams
- Idempotent handlers: Events may be processed multiple times
- Separate concerns: Different models for read and write
- Eventual consistency: Accept and communicate delay
- Monitoring: Track projection lag and event processing
- 事件命名:使用过去式、有业务意义的名称
- 事件不可变:永远不要修改已发布的事件
- 事件版本化:规划事件 schema 演进
- 快照:用于长事件流
- 幂等处理器:事件可能会被处理多次
- 关注点分离:读模型和写模型分离
- 最终一致性:接受并沟通延迟
- 监控:跟踪投影延迟和事件处理情况
Microservices
微服务
- Service size: Small enough to understand, large enough to provide value
- Data ownership: Each service owns its data
- Asynchronous communication: Prefer events over synchronous calls
- Service discovery: Dynamic service location
- Configuration management: Centralized, environment-specific config
- Deployment independence: Services deploy without coordinating
- Failure isolation: Circuit breakers and bulkheads
- Distributed tracing: Correlation IDs across service calls
- 服务大小:小到易于理解,大到能提供价值
- 数据所有权:每个服务拥有自己的数据
- 异步通信:优先使用事件而非同步调用
- 服务发现:动态服务定位
- 配置管理:集中化、环境特定的配置
- 独立部署:服务无需协调即可部署
- 故障隔离:使用断路器和舱壁
- 分布式追踪:跨服务调用的关联ID
Performance & Scalability
性能与可扩展性
- Measure first: Profile before optimizing
- Cache strategically: Right layer, right data, right TTL
- Async processing: Move slow operations to background
- Connection pooling: Reuse database/HTTP connections
- Pagination: Never return unbounded result sets
- Compression: Reduce network transfer size
- CDN usage: Serve static assets from edge locations
- Database indexes: Index query patterns, not all columns
- 先测量:在优化前先分析
- 策略性缓存:正确的层、正确的数据、正确的TTL
- 异步处理:将慢操作移到后台
- 连接池:复用数据库/HTTP连接
- 分页:永远不要返回无界结果集
- 压缩:减少网络传输大小
- CDN使用:从边缘节点提供静态资源
- 数据库索引:为查询模式建立索引,而非所有列
Security
安全
- Defense in depth: Multiple security layers
- Least privilege: Minimal permissions necessary
- Encrypt in transit: TLS for all network communication
- Encrypt at rest: Sensitive data encrypted in storage
- Input validation: Validate and sanitize all inputs
- Authentication: Verify identity (JWT, OAuth)
- Authorization: Verify permissions (RBAC, ABAC)
- Audit logging: Track security-relevant events
- 纵深防御:多个安全层
- 最小权限:仅授予必要的权限
- 传输中加密:所有网络通信使用TLS
- 静态加密:敏感数据在存储时加密
- 输入验证:验证并清理所有输入
- 身份认证:验证身份(JWT、OAuth)
- 授权:验证权限(RBAC、ABAC)
- 审计日志:跟踪与安全相关的事件
Testing
测试
- Test pyramid: Many unit tests, fewer integration, few E2E
- Test behavior: Focus on business logic, not implementation
- Contract testing: Verify API contracts between services
- Chaos engineering: Test failure scenarios
- Performance testing: Load test before production
- Security testing: Automated vulnerability scanning
- Smoke tests: Quick validation after deployment
- Canary deployments: Gradual rollout to detect issues
- 测试金字塔:大量单元测试,少量集成测试,极少端到端测试
- 测试行为:关注业务逻辑,而非实现细节
- 契约测试:验证服务间的API契约
- 混沌工程:测试故障场景
- 性能测试:在上线前进行负载测试
- 安全测试:自动化漏洞扫描
- 冒烟测试:部署后快速验证
- 金丝雀部署:逐步发布以检测问题
Monitoring & Observability
监控与可观察性
- Structured logging: JSON logs with context
- Metrics collection: RED metrics (Rate, Errors, Duration)
- Distributed tracing: Request flow across services
- Health checks: Liveness and readiness endpoints
- Alerting: Alert on symptoms, not causes
- Dashboards: Key metrics visible at a glance
- SLO/SLA: Define and track service levels
- Incident response: Runbooks for common issues
- 结构化日志:带上下文的JSON日志
- 指标收集:RED指标(Rate、Errors、Duration)
- 分布式追踪:跨服务的请求流
- 健康检查:存活和就绪端点
- 告警:针对症状而非原因告警
- 仪表盘:关键指标一目了然
- SLO/SLA:定义并跟踪服务级别
- 事件响应:常见问题的运行手册
Resources
资源
Books
书籍
- "Domain-Driven Design" by Eric Evans
- "Implementing Domain-Driven Design" by Vaughn Vernon
- "Microservices Patterns" by Chris Richardson
- "Building Microservices" by Sam Newman
- "Designing Data-Intensive Applications" by Martin Kleppmann
- 《领域驱动设计》Eric Evans
- 《实现领域驱动设计》Vaughn Vernon
- 《微服务模式》Chris Richardson
- 《构建微服务》Sam Newman
- 《设计数据密集型应用》Martin Kleppmann
Online Resources
在线资源
- https://microservices.io/patterns - Microservices pattern catalog
- https://martinfowler.com - Architecture articles and patterns
- https://learn.microsoft.com/en-us/azure/architecture - Azure Architecture Center
- https://aws.amazon.com/architecture - AWS Architecture resources
- https://cloud.google.com/architecture - Google Cloud Architecture
Skill Version: 1.0.0
Last Updated: October 2025
Skill Category: Enterprise Architecture, System Design, Distributed Systems
- https://microservices.io/patterns - 微服务模式目录
- https://martinfowler.com - 架构文章和模式
- https://learn.microsoft.com/en-us/azure/architecture - Azure架构中心
- https://aws.amazon.com/architecture - AWS架构资源
- https://cloud.google.com/architecture - Google Cloud架构
指南版本: 1.0.0
最后更新: 2025年10月
指南分类: 企业架构、系统设计、分布式系统