关于 software-architecture
面向软件开发者的架构参考资料,汇集 SOLID 原则、整洁架构、设计模式和常见反模式。通过代码示例说明职责划分、依赖关系与架构决策记录。
主要功能
- 01SOLID 原则及代码示例
- 02整洁架构的分层说明
- 03仓储、工厂、策略和观察者模式
- 04架构决策记录示例
- 05常见架构反模式
适用场景
- 01设计职责清晰的类与接口
- 02规划应用层和领域层结构
- 03选择并应用常用设计模式
- 04记录重要的软件架构决策
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ bbeierle12/ skill-mcp-claude --skill software-architecture安装后在 Claude Code 中输入/software-architecture确认已加载。
附带文件_meta.json
详细介绍
软件架构
SOLID 原则
单一职责原则(SRP)
一个类应该只有一个需要修改的理由。
// ❌ Bad: Multiple responsibilities
class User {
saveToDatabase() { }
sendEmail() { }
generateReport() { }
}
// ✅ Good: Single responsibility
class User { }
class UserRepository { save(user: User) { } }
class EmailService { send(to: string) { } }
class ReportGenerator { generate(user: User) { } }
开闭原则(OCP)
对扩展开放,对修改关闭。
// ❌ Bad: Requires modification for new types
function calculateArea(shape: Shape) {
if (shape.type === 'circle') {
return Math.PI * shape.radius ** 2;
} else if (shape.type === 'rectangle') {
return shape.width * shape.height;
}
// Need to modify for new shapes
}
// ✅ Good: Extend without modification
interface Shape {
area(): number;
}
class Circle implements Shape {
constructor(private radius: number) {}
area() { return Math.PI * this.radius ** 2; }
}
class Rectangle implements Shape {
constructor(private width: number, private height: number) {}
area() { return this.width * this.height; }
}
里氏替换原则(LSP)
子类型必须能够替换其基类型。
// ❌ Bad: Square violates Rectangle's contract
class Rectangle {
setWidth(w: number) { this.width = w; }
setHeight(h: number) { this.height = h; }
}
class Square extends Rectangle {
setWidth(w: number) { this.width = w; this.height = w; } // Violates LSP
}
// ✅ Good: Separate hierarchies
interface Shape {
area(): number;
}
class Rectangle implements Shape { }
class Square implements Shape { }
接口隔离原则(ISP)
客户端不应依赖它们不会使用的接口。
// ❌ Bad: Fat interface
interface Worker {
work(): void;
eat(): void;
sleep(): void;
}
// ✅ Good: Segregated interfaces
interface Workable { work(): void; }
interface Eatable { eat(): void; }
interface Sleepable { sleep(): void; }
class Human implements Workable, Eatable, Sleepable { }
class Robot implements Workable { }
依赖倒置原则(DIP)
依赖抽象,而非具体实现。
// ❌ Bad: High-level depends on low-level
class UserService {
private database = new MySQLDatabase();
}
// ✅ Good: Both depend on abstraction
interface Database {
save(data: any): Promise<void>;
find(id: string): Promise<any>;
}
class UserService {
constructor(private database: Database) {}
}
// Now can inject any implementation
const service = new UserService(new MySQLDatabase());
const testService = new UserService(new MockDatabase());
整洁架构
┌─────────────────────────────────────────────────┐
│ Frameworks │
│ (Express, React, Database Drivers) │
├─────────────────────────────────────────────────┤
│ Interface Adapters │
│ (Controllers, Presenters, Gateways) │
├─────────────────────────────────────────────────┤
│ Application Layer │
│ (Use Cases, Application Services) │
├─────────────────────────────────────────────────┤
│ Domain Layer │
│ (Entities, Value Objects, Domain Services) │
└─────────────────────────────────────────────────┘
Dependencies point INWARD only.
Inner layers know nothing about outer layers.
领域层(最内层)
// entities/User.ts
export class User {
constructor(
public readonly id: string,
public readonly email: Email,
public name: string,
private password: HashedPassword
) {}
changeName(newName: string): void {
if (newName.length < 2) {
throw new DomainError('Name too short');
}
this.name = newName;
}
verifyPassword(plaintext: string): boolean {
return this.password.verify(plaintext);
}
}
// value-objects/Email.ts
export class Email {
constructor(private readonly value: string) {
if (!this.isValid(value)) {
throw new DomainError('Invalid email');
}
}
private isValid(email: string): boolean {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
toString(): string {
return this.value;
}
}
应用层(用例)
// use-cases/CreateUser.ts
interface CreateUserInput {
email: string;
password: string;
name: string;
}
interface CreateUserOutput {
id: string;
email: string;
name: string;
}
export class CreateUserUseCase {
constructor(
private userRepository: UserRepository,
private passwordHasher: PasswordHasher,
private emailService: EmailService
) {}
async execute(input: CreateUserInput): Promise<CreateUserOutput> {
// Validate email uniqueness
const existing = await this.userRepository.findByEmail(input.email);
if (existing) {
throw new ApplicationError('Email already exists');
}
// Create domain object
const hashedPassword = await this.passwordHasher.hash(input.password);
const user = new User(
generateId(),
new Email(input.email),
input.name,
hashedPassword
);
// Persist
await this.userRepository.save(user);
// Side effects
await this.emailService.sendWelcome(user.email);
return {
id: user.id,
email: user.email.toString(),
name: user.name,
};
}
}
接口适配器
// controllers/UserController.ts
export class UserController {
constructor(private createUserUseCase: CreateUserUseCase) {}
async create(req: Request, res: Response): Promise<void> {
const result = await this.createUserUseCase.execute({
email: req.body.email,
password: req.body.password,
name: req.body.name,
});
res.status(201).json(result);
}
}
// repositories/PrismaUserRepository.ts
export class PrismaUserRepository implements UserRepository {
async save(user: User): Promise<void> {
await prisma.user.create({
data: {
id: user.id,
email: user.email.toString(),
name: user.name,
},
});
}
async findByEmail(email: string): Promise<User | null> {
const data = await prisma.user.findUnique({
where: { email },
});
return data ? this.toDomain(data) : null;
}
private toDomain(data: PrismaUser): User {
return new User(
data.id,
new Email(data.email),
data.name,
new HashedPassword(data.password)
);
}
}
设计模式
仓储模式
interface Repository<T> {
findById(id: string): Promise<T | null>;
findAll(): Promise<T[]>;
save(entity: T): Promise<void>;
delete(id: string): Promise<void>;
}
工厂模式
interface PaymentProcessor {
process(amount: number): Promise<void>;
}
class PaymentProcessorFactory {
create(type: 'stripe' | 'paypal'): PaymentProcessor {
switch (type) {
case 'stripe': return new StripeProcessor();
case 'paypal': return new PayPalProcessor();
}
}
}
策略模式
interface PricingStrategy {
calculate(basePrice: number): number;
}
class RegularPricing implements PricingStrategy {
calculate(basePrice: number) { return basePrice; }
}
class PremiumPricing implements PricingStrategy {
calculate(basePrice: number) { return basePrice * 0.9; }
}
class Order {
constructor(private pricingStrategy: PricingStrategy) {}
getTotal(basePrice: number) {
return this.pricingStrategy.calculate(basePrice);
}
}
观察者模式
interface Observer<T> {
update(data: T): void;
}
class EventEmitter<T> {
private observers: Observer<T>[] = [];
subscribe(observer: Observer<T>): void {
this.observers.push(observer);
}
notify(data: T): void {
this.observers.forEach(o => o.update(data));
}
}
架构决策记录(ADR)
记录重要决策:
# ADR-001: Use PostgreSQL for Primary Database
## Status
Accepted
## Context
Need to choose a primary database for user data storage.
## Decision
Use PostgreSQL.
## Consequences
### Positive
- Strong consistency guarantees
- Rich query capabilities
- Well-supported ORMs
### Negative
- Requires more operational overhead than SQLite
- Horizontal scaling more complex than NoSQL
应避免的反模式
上帝对象
❌ 一个包办所有事情的类
意大利面条式代码
❌ 缺乏清晰的结构或流程
金锤反模式
❌ 对每个问题都使用相同的解决方案
过早优化
❌ 未经测量就进行优化
货物崇拜式编程
❌ 不理解原因就套用模式
评论
支持 Markdown