You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TypeScript后端Branch类优化:DRY原则落地与可复用自定义错误方案

优化方案:遵循DRY原则重构Branch类的验证与清理逻辑

一、合并自定义错误类为通用验证异常

将十几个独立的错误类替换为一个可配置的ValidationException,通过参数区分不同错误场景,既保留错误上下文清晰度,又减少冗余类文件:

// src/entities/Branch/exceptions/Validation.exception.ts
export class ValidationException extends Error {
  constructor(
    public readonly field: string,
    public readonly rule: string,
    message: string
  ) {
    super(message);
    this.name = "ValidationException";
    Object.setPrototypeOf(this, ValidationException.prototype);
  }
}

原NameLengthCannotBeZeroException可替换为:

throw new ValidationException("name", "minLength", "分支名称不能为空");

二、提取通用清理方法

所有字符串类型字段的清理逻辑完全一致,提取为通用静态方法;针对数字类型单独处理:

// 通用字符串清理
private static sanitizeString(value: string): string {
  return value
    .split("\n")[0]
    .replace(/^[\r\t\f ]+|[\r\t\f ]+$/g, "")
    .replace(/[\t\f ]+/g, " ");
}

// 数字类型清理(处理streetNumber)
private static sanitizeNumber(value: number | null): number | null {
  return value === null ? value : Math.floor(value);
}

三、抽象通用验证规则

封装重复的验证逻辑为可复用函数,接收字段名、值和规则参数:

// 字符串长度验证(支持最小/最大长度)
private static validateStringLength(
  field: string,
  value: string,
  min?: number,
  max?: number
): void {
  if (min !== undefined && value.length < min) {
    throw new ValidationException(
      field,
      "minLength",
      `${field}长度不能小于${min}`
    );
  }
  if (max !== undefined && value.length > max) {
    throw new ValidationException(
      field,
      "maxLength",
      `${field}长度不能超过${max}`
    );
  }
}

// 正整数验证(允许null)
private static validatePositiveNumber(
  field: string,
  value: number | null
): void {
  if (value !== null && value <= 0) {
    throw new ValidationException(
      field,
      "positiveNumber",
      `${field}不能小于或等于0`
    );
  }
}

四、配置化批量处理字段

定义字段配置对象映射每个字段的清理/验证规则,通过循环批量处理,彻底消除重复方法:

// 字段配置类型定义
type FieldConfig<T> = {
  sanitize?: (value: T) => T;
  validate?: (field: string, value: T) => void;
};

// Branch字段的验证与清理配置
private static readonly fieldConfigs: Record<keyof Branch, FieldConfig<any>> = {
  name: {
    sanitize: Branch.sanitizeString,
    validate: (field, value) => Branch.validateStringLength(field, value, 1, 200)
  },
  streetAddress: {
    sanitize: Branch.sanitizeString,
    validate: (field, value) => Branch.validateStringLength(field, value, undefined, 50)
  },
  streetNumber: {
    sanitize: Branch.sanitizeNumber,
    validate: Branch.validatePositiveNumber
  },
  streetComplement: {
    sanitize: Branch.sanitizeString,
    validate: (field, value) => Branch.validateStringLength(field, value, undefined, 50)
  },
  neighborhood: {
    sanitize: Branch.sanitizeString,
    validate: (field, value) => Branch.validateStringLength(field, value, undefined, 50)
  },
  city: {
    sanitize: Branch.sanitizeString,
    validate: (field, value) => Branch.validateStringLength(field, value, undefined, 50)
  },
  state: {
    sanitize: Branch.sanitizeString,
    validate: (field, value) => Branch.validateStringLength(field, value, undefined, 50)
  },
  country: {
    sanitize: Branch.sanitizeString,
    validate: (field, value) => Branch.validateStringLength(field, value, undefined, 50)
  }
};

// 通用字段处理方法
private static processField<T>(field: keyof Branch, value: T): T {
  const config = Branch.fieldConfigs[field];
  if (!config) return value;

  const sanitizedValue = config.sanitize ? config.sanitize(value) : value;
  config.validate?.(field as string, sanitizedValue);

  return sanitizedValue;
}

五、重构实例创建逻辑

添加工厂方法create,在实例化时自动执行所有字段的清理与验证:

static create(branchData: Omit<Branch, "id" | "createdAt" | "updatedAt">): Branch {
  const branch = new Branch();

  // 处理需要验证的字段
  branch.name = Branch.processField("name", branchData.name);
  branch.streetAddress = Branch.processField("streetAddress", branchData.streetAddress);
  branch.streetNumber = Branch.processField("streetNumber", branchData.streetNumber);
  branch.streetComplement = Branch.processField("streetComplement", branchData.streetComplement);
  branch.neighborhood = Branch.processField("neighborhood", branchData.neighborhood);
  branch.city = Branch.processField("city", branchData.city);
  branch.state = Branch.processField("state", branchData.state);
  branch.country = Branch.processField("country", branchData.country);

  // 赋值其他无需验证的字段
  branch.active = branchData.active;
  branch.createdBy = branchData.createdBy;
  branch.createdFrom = branchData.createdFrom;
  branch.updatedBy = branchData.updatedBy;
  branch.updatedFrom = branchData.updatedFrom;
  branch.environment = branchData.environment;
  branch.reportLogo = branchData.reportLogo;
  branch.reportFinalMessage = branchData.reportFinalMessage;
  branch.cnpj = branchData.cnpj;
  branch.latitude = branchData.latitude;
  branch.longitude = branchData.longitude;
  branch.zipCode = branchData.zipCode;

  // 设置时间戳
  const now = new Date();
  branch.createdAt = now;
  branch.updatedAt = now;

  return branch;
}

优化优势

  1. 彻底遵循DRY原则:消除大量重复的sanitize/validate方法,新增字段只需添加配置
  2. 错误类复用:用一个通用异常类替代十几个独立错误类,同时保留错误上下文
  3. 可维护性提升:验证规则集中管理,修改规则只需调整通用函数或配置
  4. 类型安全:通过TypeScript的keyof和泛型保证字段处理的类型正确性

内容的提问来源于stack exchange,提问作者Cristofer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 15:35:57