发布于 2026-01-06 9 阅读
0

All Design Pattern Javascript/Typescript You Must Know Structural Patterns with Best Practice API Usage Behavioral Design Patterns with Best Practice API Workflows Full-Stack TypeScript Clean Architecture Plan DEV's Worldwide Show and Tell Challenge Presented by Mux: Pitch Your Projects!

你必须了解的所有 JavaScript/TypeScript 设计模式

结构化模式与最佳实践 API 使用

行为设计模式与最佳实践 API 工作流

全栈 TypeScript 整洁架构计划

由 Mux 主办的 DEV 全球展示挑战赛:展示你的项目!

结构化模式与最佳实践 API 使用


1. 复合模式 + API 获取

最佳实践用例:想象一个由组件

(例如小部件、仪表板) 组成的系统,其中每个小部件都获取自己的 API 数据。

图片描述

例子:

interface Component {
  render(): Promise<void>;
}

class APITile implements Component {
  constructor(private url: string) {}

  async render() {
    const res = await fetch(this.url);
    const data = await res.json();
    console.log("Tile data:", data);
  }
}

class Dashboard implements Component {
  private children: Component[] = [];

  add(component: Component) {
    this.children.push(component);
  }

  async render() {
    console.log("Rendering Dashboard...");
    await Promise.all(this.children.map(child => child.render()));
  }
}

// Usage
const dashboard = new Dashboard();
dashboard.add(new APITile("https://jsonplaceholder.typicode.com/users/1"));
dashboard.add(new APITile("https://jsonplaceholder.typicode.com/posts/1"));

dashboard.render();
Enter fullscreen mode Exit fullscreen mode

最佳实践:

  • 请正确使用async/await——如果需要,请始终将 API 调用包装在 try-catch 中。
  • 每个组件负责自己的数据获取

2. 装饰器模式 + Axios 日志装饰器

最佳实践用例:在不触及核心逻辑的情况下,使用日志记录、缓存或重试装饰器

包装 API 调用。

图片描述

例子:

import axios from 'axios';

interface APIService {
  get(url: string): Promise<any>;
}

class RealAPIService implements APIService {
  async get(url: string) {
    const response = await axios.get(url);
    return response.data;
  }
}

class LoggingDecorator implements APIService {
  constructor(private service: APIService) {}

  async get(url: string) {
    console.log(`[LOG] Fetching: ${url}`);
    const result = await this.service.get(url);
    console.log(`[LOG] Fetched:`, result);
    return result;
  }
}

// Usage
const api = new LoggingDecorator(new RealAPIService());
api.get('https://jsonplaceholder.typicode.com/users/1');
Enter fullscreen mode Exit fullscreen mode

最佳实践:

  • 使用装饰器可以透明地增强行为。
  • 保持API 服务和横切关注点解耦

3. 适配器模式 + API 集成

最佳实践用例:

旧版或第三方 API 响应适配到应用程序的预期格式时。

图片描述

例子:

interface User {
  id: number;
  fullName: string;
}

class UserAPI {
  async fetchUser(): Promise<any> {
    const response = await fetch('https://jsonplaceholder.typicode.com/users/1');
    return response.json();
  }
}

class UserAdapter {
  constructor(private api: UserAPI) {}

  async getUser(): Promise<User> {
    const data = await this.api.fetchUser();
    return {
      id: data.id,
      fullName: `${data.name}`
    };
  }
}

// Usage
const userAdapter = new UserAdapter(new UserAPI());
userAdapter.getUser().then(user => console.log(user));
Enter fullscreen mode Exit fullscreen mode

最佳实践:

  • 使用适配器将你的领域模型与外部 API 解耦
  • 即使外部 API 不一致,也要确保内部接口的一致性。

4. 桥接模式 + 用于多个后端的 API

最佳实践用例:

支持在通用抽象层背后实现多个后端(例如 REST 和 GraphQL)。

图片描述

例子:

interface UserService {
  getUser(id: number): Promise<any>;
}

class RestUserService implements UserService {
  async getUser(id: number) {
    const res = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`);
    return res.json();
  }
}

class GraphQLUserService implements UserService {
  async getUser(id: number) {
    const query = `{ user(id: ${id}) { id, name } }`;
    const res = await fetch('/graphql', {
      method: 'POST',
      body: JSON.stringify({ query }),
      headers: { 'Content-Type': 'application/json' }
    });
    const data = await res.json();
    return data.data.user;
  }
}

// Usage
function displayUser(service: UserService, id: number) {
  service.getUser(id).then(console.log);
}

displayUser(new RestUserService(), 1);
// displayUser(new GraphQLUserService(), 1);
Enter fullscreen mode Exit fullscreen mode

最佳实践:

  • 使用通用的服务接口
  • 为了便于将来灵活使用,请保持代码与后端无关

5. 外观模式 + 统一 API 服务

最佳实践用例:

将复杂的多端点操作简化为一个简洁的界面。

图片描述

例子:

class UserAPI {
  getUser(id: number) {
    return fetch(`https://jsonplaceholder.typicode.com/users/${id}`).then(res => res.json());
  }
}

class PostAPI {
  getPost(id: number) {
    return fetch(`https://jsonplaceholder.typicode.com/posts/${id}`).then(res => res.json());
  }
}

class APIServiceFacade {
  private userAPI = new UserAPI();
  private postAPI = new PostAPI();

  async getUserWithPost(userId: number, postId: number) {
    const [user, post] = await Promise.all([
      this.userAPI.getUser(userId),
      this.postAPI.getPost(postId)
    ]);
    return { user, post };
  }
}

// Usage
const apiFacade = new APIServiceFacade();
apiFacade.getUserWithPost(1, 1).then(console.log);
Enter fullscreen mode Exit fullscreen mode

最佳实践:

  • 向应用层公开简单、高级的 API 。
  • 隐藏底层多API编排的复杂性。

6. 代理模式 + 延迟加载 API

最佳实践用例:

仅在需要时延迟加载 API 数据,或缓存结果。

图片描述

例子:

interface DataService {
  getData(): Promise<any>;
}

class RealDataService implements DataService {
  async getData() {
    const res = await fetch("https://jsonplaceholder.typicode.com/posts/1");
    return res.json();
  }
}

class ProxyDataService implements DataService {
  private realService: RealDataService | null = null;

  async getData() {
    if (!this.realService) {
      console.log("Initializing RealDataService...");
      this.realService = new RealDataService();
    }
    return this.realService.getData();
  }
}

// Usage
const service = new ProxyDataService();
service.getData().then(console.log);
Enter fullscreen mode Exit fullscreen mode

最佳实践:

  • 使用代理进行缓存、延迟加载或API 调用访问控制。
  • 推迟资源密集型操作,直到必要时才进行。

摘要:简洁的 API 驱动型结构模式

图案 最佳 API 使用场景
合成的 独立的嵌套组件各自获取各自的 API
装饰师 透明日志记录、缓存、API 调用重试机制
适配器 将外部 API 响应适配到您自己的模型中
在 REST、GraphQL 或其他后端之间切换
正面 简化单一服务背后的多端点编排
代理人 在代理服务后延迟加载或缓存 API 数据

最终最佳实践(中高级)

  • 始终将 API 端点抽象在接口之后(清晰的依赖倒置)
  • 使用装饰器/代理进行日志记录、缓存、重试和授权。
  • 外观模式和适配器非常适合安全地升级第三方或传统 API。
  • 巧妙地使用async/await 和 Promise.all,针对并行请求进行优化
  • 将SOLID 原则与这些模式结合使用,可以实现简洁、可扩展的设计。

行为设计模式与最佳实践 API 工作流

行为模式管理对象如何交互、协调以及共同执行操作。在 API 驱动的应用程序中,这通常意味着:

  • 处理异步调用
  • 连锁动作
  • 管理重试、命令、事件系统
  • 集中协调

我将展示一些你可以立即应用到项目中的简洁示例。


1. 责任链 + API 重试链

何时使用:

当多个处理程序/处理器有机会处理 API 请求时,例如使用备用 URL 重试。

例子:

interface APIHandler {
  setNext(handler: APIHandler): APIHandler;
  handle(url: string): Promise<any>;
}

class PrimaryAPIHandler implements APIHandler {
  private nextHandler: APIHandler | null = null;

  setNext(handler: APIHandler): APIHandler {
    this.nextHandler = handler;
    return handler;
  }

  async handle(url: string): Promise<any> {
    try {
      const res = await fetch(url);
      if (!res.ok) throw new Error("Primary failed");
      return res.json();
    } catch {
      if (this.nextHandler) return this.nextHandler.handle(url);
      throw new Error("All attempts failed");
    }
  }
}

class FallbackAPIHandler implements APIHandler {
  async handle(url: string): Promise<any> {
    console.log("Trying fallback...");
    const fallbackUrl = url.replace("primary", "fallback");
    const res = await fetch(fallbackUrl);
    if (!res.ok) throw new Error("Fallback failed");
    return res.json();
  }
}

// Usage
const primary = new PrimaryAPIHandler();
primary.setNext(new FallbackAPIHandler());

primary.handle("https://primary.api/users/1")
  .then(data => console.log(data))
  .catch(err => console.error(err));
Enter fullscreen mode Exit fullscreen mode

最佳实践:

  • 清晰地链式处理程序——不要在单个方法中硬编码重试逻辑。
  • 优雅地处理失败,并提供备用方案。

2. 命令模式 + 排队的 API 调用

何时使用:

将 API 操作封装为命令对象,从而实现排队、撤销或批量处理。

例子:

interface Command {
  execute(): Promise<void>;
}

class FetchUserCommand implements Command {
  constructor(private userId: number) {}

  async execute() {
    const res = await fetch(`https://jsonplaceholder.typicode.com/users/${this.userId}`);
    const data = await res.json();
    console.log("User:", data);
  }
}

class APICommandQueue {
  private queue: Command[] = [];

  addCommand(command: Command) {
    this.queue.push(command);
  }

  async run() {
    for (const command of this.queue) {
      await command.execute();
    }
  }
}

// Usage
const queue = new APICommandQueue();
queue.addCommand(new FetchUserCommand(1));
queue.addCommand(new FetchUserCommand(2));
queue.run();
Enter fullscreen mode Exit fullscreen mode

最佳实践:

  • 使用命令队列进行离线队列、重试系统或批量作业处理
  • 保持命令简洁明了,并且内容独立完整。

3. 观察者模式 + API 轮询/WebSocket 事件

何时使用:

当应用程序的多个部分需要对 API 事件或服务器更新做出反应时。

例子:

type Listener = (data: any) => void;

class APIObserver {
  private listeners: Listener[] = [];

  subscribe(listener: Listener) {
    this.listeners.push(listener);
  }

  unsubscribe(listener: Listener) {
    this.listeners = this.listeners.filter(l => l !== listener);
  }

  notify(data: any) {
    this.listeners.forEach(listener => listener(data));
  }

  async pollAPI() {
    setInterval(async () => {
      const res = await fetch("https://jsonplaceholder.typicode.com/posts/1");
      const data = await res.json();
      this.notify(data);
    }, 5000);
  }
}

// Usage
const observer = new APIObserver();
observer.subscribe(data => console.log("Subscriber 1", data));
observer.subscribe(data => console.log("Subscriber 2", data));
observer.pollAPI();
Enter fullscreen mode Exit fullscreen mode

最佳实践:

  • 使用观察者进行基于推送的 API 更新(WebSocket、轮询)
  • 将事件源与事件处理程序解耦。

4. 策略模式 + 动态 API 请求策略

何时使用:

在运行时选择不同的 API 请求策略(例如,缓存、无缓存、重试)。

例子:

interface FetchStrategy {
  fetch(url: string): Promise<any>;
}

class NormalFetch implements FetchStrategy {
  async fetch(url: string) {
    const res = await fetch(url);
    return res.json();
  }
}

class CachedFetch implements FetchStrategy {
  private cache: Map<string, any> = new Map();

  async fetch(url: string) {
    if (this.cache.has(url)) {
      console.log("Returning from cache");
      return this.cache.get(url);
    }
    const res = await fetch(url);
    const data = await res.json();
    this.cache.set(url, data);
    return data;
  }
}

class APIContext {
  constructor(private strategy: FetchStrategy) {}

  fetch(url: string) {
    return this.strategy.fetch(url);
  }

  setStrategy(strategy: FetchStrategy) {
    this.strategy = strategy;
  }
}

// Usage
const api = new APIContext(new NormalFetch());
api.fetch("https://jsonplaceholder.typicode.com/users/1").then(console.log);

api.setStrategy(new CachedFetch());
api.fetch("https://jsonplaceholder.typicode.com/users/1").then(console.log);
Enter fullscreen mode Exit fullscreen mode

最佳实践:

  • 使用策略实现可切换的 API 请求行为,而无需更改客户端逻辑。

5. 中介者模式 + 集中式 API 协调器

何时使用:

用于集中管理不同服务或组件之间通过 API 调用进行的通信。

例子:

class APIMediator {
  private services: Record<string, any> = {};

  register(name: string, service: any) {
    this.services[name] = service;
  }

  async notify(sender: string, action: string, ...args: any[]) {
    switch (action) {
      case "userFetched":
        this.services["PostService"].fetchPosts(args[0]);
        break;
    }
  }
}

class UserService {
  constructor(private mediator: APIMediator) {}

  async fetchUser(id: number) {
    const res = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`);
    const user = await res.json();
    console.log("User fetched:", user);
    this.mediator.notify("UserService", "userFetched", user.id);
  }
}

class PostService {
  async fetchPosts(userId: number) {
    const res = await fetch(`https://jsonplaceholder.typicode.com/posts?userId=${userId}`);
    const posts = await res.json();
    console.log("Posts fetched:", posts);
  }
}

// Usage
const mediator = new APIMediator();
const userService = new UserService(mediator);
const postService = new PostService();

mediator.register("PostService", postService);

userService.fetchUser(1);
Enter fullscreen mode Exit fullscreen mode

最佳实践:

  • 调解员可以防止服务之间的紧密联系——一项服务不需要知道接下来该联系谁。

摘要——何时选择哪种 API 模式

图案 API 工作流程示例
责任链 重试链、回退 API、拦截器
命令 API 命令队列,可撤销操作
观察者 WebSocket 通知、API 轮询监听器
战略 动态 API 请求行为(缓存、非缓存)
调解员 各部门之间的集中协调

最终版 API 最佳实践

  • 始终使用简洁的接口或类将 API 调用封装在服务中。
  • 始终使用async/await对于并行请求使用Promise.all 。
  • 利用重试机制、回退处理程序和观察器构建弹性系统
  • 应用SOLID 原则和模式,使 API 密集型应用程序保持模块化和可测试性。
  • 使用中介器和命令队列来处理工作流和后台作业。

全栈 TypeScript 整洁架构计划

技术栈:

  • 后端: NestJS(TypeScript,模块化,支持依赖注入)
  • 前端: Next.js(React + TypeScript)
  • API协议: REST(可选扩展为GraphQL)
  • 模式:工厂模式、单例模式、适配器模式、装饰器模式、代理模式、策略模式、责任链模式、命令模式、观察者模式、中介者模式
  • 工具: Axios、Prisma 或 TypeORM、Zod/DTO 验证、Swagger、JWT

1. 模块化 API 层(后端 — NestJS)

后端应用模块

src/
  modules/
    user/
      user.module.ts
      user.controller.ts
      user.service.ts
      user.repository.ts
      dto/
      strategy/
      observer/
    post/
    auth/
  common/
    middleware/
    guards/
    interceptors/
    exceptions/
    config/
  main.ts
Enter fullscreen mode Exit fullscreen mode

最佳实践:

  • 保持模块独立性
  • 实现中间件链
  • 集中式错误处理
  • 使用DTO + 验证
  • 隔离存储库
  • 在服务中应用设计模式

2. 示例:API 重试链(责任链)

/common/api/api-retry.handler.ts

export interface APIHandler {
  setNext(handler: APIHandler): APIHandler;
  handle(url: string): Promise<any>;
}

export class PrimaryHandler implements APIHandler {
  private nextHandler: APIHandler | null = null;

  setNext(handler: APIHandler): APIHandler {
    this.nextHandler = handler;
    return handler;
  }

  async handle(url: string) {
    try {
      return await fetch(url).then(res => res.json());
    } catch {
      if (this.nextHandler) return this.nextHandler.handle(url);
      throw new Error('API failed');
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

在你的 NestJS服务中使用它


3. API 命令队列(命令模式)

/common/commands/api-command.ts

export interface Command {
  execute(): Promise<void>;
}

export class GetUserCommand implements Command {
  constructor(private userService: UserService, private id: number) {}

  async execute() {
    const user = await this.userService.getUserById(this.id);
    console.log('User fetched:', user);
  }
}
Enter fullscreen mode Exit fullscreen mode

使用队列来处理这些任务,以便进行后台作业或顺序工作流程。


4. API观察者服务

/modules/post/post.observer.ts

type Listener = (data: any) => void;

export class PostObserver {
  private listeners: Listener[] = [];

  subscribe(listener: Listener) {
    this.listeners.push(listener);
  }

  notify(data: any) {
    this.listeners.forEach(listener => listener(data));
  }
}
Enter fullscreen mode Exit fullscreen mode

创建新帖子时通知其他模块。


5. 授权策略模式

/modules/auth/strategy/jwt.strategy.ts

@Injectable()
export class JWTStrategy extends PassportStrategy(Strategy) {
  constructor(private configService: ConfigService) {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      secretOrKey: configService.get('JWT_SECRET'),
    });
  }

  async validate(payload: any) {
    return { userId: payload.sub, email: payload.email };
  }
}
Enter fullscreen mode Exit fullscreen mode

最佳实践:

  • 解耦认证策略(JWT、会话、OAuth)
  • 保持身份验证策略可插拔

6. 错误处理(代理 + 装饰器)

全局异常筛选器

@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
  catch(exception: any, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();

    response.status(exception.status || 500).json({
      statusCode: exception.status || 500,
      message: exception.message || 'Unexpected error occurred',
    });
  }
}
Enter fullscreen mode Exit fullscreen mode

最佳实践:

  • 集中化错误格式化
  • 使用拦截器或装饰器进行日志记录和验证

7. 中间件、守卫、拦截器

/common/middleware/logging.middleware.ts

@Injectable()
export class LoggingMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: NextFunction) {
    console.log(`[${req.method}] ${req.originalUrl}`);
    next();
  }
}
Enter fullscreen mode Exit fullscreen mode

/common/guards/auth.guard.ts

@Injectable()
export class AuthGuard implements CanActivate {
  canActivate(context: ExecutionContext) {
    const request = context.switchToHttp().getRequest();
    return !!request.user;
  }
}
Enter fullscreen mode Exit fullscreen mode

最佳实践:

  • 派卫兵保护路线
  • 使用拦截器进行请求/响应转换
  • 在生命周期的早期应用中间件链

8. Next.js 前端架构

/services/userService.ts

import axios from 'axios';

export const userService = {
  async getUser(id: number) {
    const res = await axios.get(`/api/users/${id}`);
    return res.data;
  }
}
Enter fullscreen mode Exit fullscreen mode

/hooks/useUser.ts

import { useEffect, useState } from 'react';
import { userService } from '../services/userService';

export function useUser(id: number) {
  const [user, setUser] = useState(null);

  useEffect(() => {
    userService.getUser(id).then(setUser);
  }, [id]);

  return user;
}
Enter fullscreen mode Exit fullscreen mode

最佳实践:

  • 保持服务隔离
  • 使用React Hooks处理有状态 API 数据
  • 使用装饰器进行日志记录,或在服务上使用重试包装器。

9. 全栈集成模式

问题 模式解决方案
API重试/回退 责任链
中央事件管理 调解员 + 观察员
授权策略 策略模式
API 排队和批处理 命令模式
模块化服务协调 中介模式
惰性或缓存 API 调用 代理 + 装饰器
错误处理 + 日志记录 拦截器 + 全局过滤器
可定制请求 装饰器 + 适配器
前端服务隔离 立面 + 单行

文章来源:https://dev.to/tak089/typescript-design-patterns-5hei