Node.js使用类与建造者模式构建MySQL模型的参数校验及交互问题
问题1:入参校验与isAdmin默认值实现
首先在User类构造函数中初始化isAdmin默认值为false,参数校验分两层实现:单个setter中做参数合法性校验,build方法中做必填项的最终校验,非必填参数仅在传入有效值时才校验赋值。改造后的实现代码如下:
// Basic class User { constructor(fname) { this.fname = fname; // 初始化isAdmin默认值为false this.isAdmin = false; }; } // Builder for the user class module.exports = class UserBuilder { constructor(fname) { // 构造函数先校验必填的fname if(!fname || typeof fname !== 'string' || fname.trim().length === 0) { throw new Error('用户姓氏不能为空') } this.user = new User(fname.trim()); } setLname(lname) { // 非必填参数,传值才校验赋值 if(lname) { if(typeof lname !== 'string' || lname.trim().length === 0) { throw new Error('用户名字格式非法') } this.user.lname = lname.trim(); } return this; } setEmail(email) { if(email) { const emailReg = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if(!emailReg.test(email)) { throw new Error('邮箱格式非法') } this.user.email = email.trim(); } return this; } setPassword(password) { if(password) { if(typeof password !== 'string' || password.length < 6) { throw new Error('密码长度不能少于6位') } this.user.password = password; } return this; } setAge(age) { if(age !== undefined && age !== null) { const ageNum = Number(age); if(isNaN(ageNum) || ageNum < 0 || ageNum > 120 || !Number.isInteger(ageNum)) { throw new Error('年龄必须是0-120之间的整数') } this.user.age = ageNum; } return this; } setGender(gender) { if(gender) { if(!['男','女','保密'].includes(gender)) { throw new Error('性别参数非法') } this.user.gender = gender; } return this; } setAvatar(avatar) { if(avatar) { if(typeof avatar !== 'string' || !avatar.startsWith('http')) { throw new Error('头像地址必须为合法URL') } this.user.avatar = avatar.trim(); } return this; } setPhoneNumber(number) { if(number) { const phoneReg = /^1[3-9]\d{9}$/; if(!phoneReg.test(number)) { throw new Error('手机号格式非法') } this.user.number = number.trim(); } return this; } setIsAdmin(flag) { this.user.isAdmin = Boolean(flag); return this; } build() { // 可根据业务需求补充必填项校验,例如注册场景要求邮箱必填则放开下方注释 // if(!this.user.email) { // throw new Error('用户邮箱为必填项') // } return this.user; } }
测试时如果传入非法参数会直接抛出明确的错误,便于问题定位,你提供的测试代码可以正常运行,打印出的User实例会默认携带isAdmin: false属性。
问题2:业务逻辑与数据库交互的分层归属
按照单一职责的分层原则,数据库交互、基础的用户对象增删改操作应当封装在模型类内部,不要放在控制器中:
- 模型层负责数据定义、校验、持久化,你可以在User类中新增
save()实例方法实现用户数据的写入/更新,新增static delete(userId)静态方法实现用户删除,所有SQL操作都收拢在模型层,后续表结构调整只需修改模型代码即可,不会影响上层调用 - 控制器仅负责处理请求参数、调用模型方法、处理业务侧逻辑(比如用户注册后发送欢迎邮件)、组装返回响应,不要直接在控制器中写SQL,避免代码冗余、维护成本升高
如果后续业务复杂度提升,可以在控制器和模型之间新增Service层封装复用性高的业务逻辑,进一步拆分职责。
内容的提问来源于stack exchange,提问作者daniel sas
相关产品推荐
相关产品推荐

