6.架构设计场景题 来源:https://w0hog67yl81.feishu.cn/wiki/I8ckwv6dXiUmYqkPxQac3fmYnib 采集状态:已完成正文采集;逐段滚动采集正文与代码。 开场话术​ ​ ​ "这个功能涉及到多个业务模块,所以架构设计是核心考虑点。"​ ​ "当时我们考虑了扩展性和维护性,设计了一套可复用的架构。"​ ​ ​ ​ ​ 标准答题示例​ ​ 我们要做一个多租户的权限管理系统,需要支持不同客户的个性化配置。​ ​ 架构上我采用了分层设计:​ 1. 数据层:统一的权限模型,支持租户隔离​ 2. 服务层:可配置的权限引擎,支持规则自定义 ​ 3. 展示层:组件化的权限控制UI​ ​ 具体实现中,我用了几个关键模式:​ • 策略模式处理不同租户的权限规则​ • 装饰器模式做权限拦截和日志记录​ • 发布订阅模式处理权限变更的实时同步​ ​ 前端用React+TypeScript,状态管理用Redux Toolkit:​ • 权限数据用Normalized结构存储,便于查询和更新​ • 封装了usePermission Hook,组件直接调用判断权限​ • 路由层面做了权限守卫,无权限自动跳转​ ​ 这套架构支撑了30+租户的个性化需求,代码复用率达到85%。​ ​ ​ ​ ​ 常见追问及应对思路​ ​ 面试官: “你说的分层设计具体是怎么划分的?”​ ​ ​ 📔 答题思路:详细说明每层的职责划分、调用关系以及如何避免耦合。​ ​ 我们是按照领域驱动设计(DDD)的思路做的分层,整体结构分为:数据层(Domain) → 服务层(Service) → 展示层(Presentation),每层只依赖下层,接口调用清晰,避免循环依赖。​ ​ 数据层(Domain Layer):​ 定义业务实体、权限模型和核心结构,不涉及任何业务逻辑,只关注数据的结构和约束。​ interface Permission { id: string; resource: string; action: string; conditions?: Record; } interface Role { id: string; name: string; permissions: Permission[]; tenantId: string; } ​ 服务层(Service Layer):​ 负责权限的具体业务逻辑,比如权限校验、角色获取、权限更新等,同时隐藏实现细节,对外提供清晰接口。​ class PermissionService { checkPermission(userId: string, resource: string, action: string): boolean getRolesByUser(userId: string): Role[] updateUserPermissions(userId: string, permissions: Permission[]): void } 服务层内部也做了权限缓存、并发控制等处理,提升了性能。​ ​ 展示层(Presentation Layer):​ 在组件中消费权限服务,结合 Hook 做权限渲染控制,不直接操作业务逻辑。​ const ProtectedComponent = ({ resource, action, children }) => { const hasPermission = usePermission(resource, action); return hasPermission ? children : ; }; 此外,我们还通过事件机制让服务层可以通知上层做界面刷新,保持了良好的解耦性。​ ​ ​ ​ ​ 面试官: “多租户的数据隔离是怎么保证的?”​ ​ 📔 答题思路:从数据库、应用、前端、测试四个层级说明如何防止数据串租。​ ​ 多租户场景下,我们用到了强制型隔离机制,分为四层:​ ​ 数据库层面:​ • 所有数据表设计都加了 tenant_id 字段;​ • 所有 ORM 查询都自动注入 WHERE tenant_id = ?;​ • 对外查询提供只读视图,防止跨租户查询;​ • 敏感表额外做了行级权限控制。​ ​ 应用层面:​ 封装了一个 TenantContext,让所有查询都自动注入租户条件,防止开发者遗漏。​ return this.db.query( sql + ' AND tenant_id = ?', [...params, this.tenantId] ); 还统一对外暴露只带 tenant 权限的服务接口,避免绕过隔离逻辑。​ ​ 前端层面:​ • 登录成功后将租户 ID 存入全局 Context;​ • 所有 API 请求统一加上 X-Tenant-ID Header;​ • 页面路由前会做权限校验,防止用户跳转其他租户页面。​ ​ 测试验证:​ • 做了多租户账号并发操作的集成测试; ​ • 模拟跨租户非法请求(如改 header),验证服务端防护;​ • 使用 Postman/自动化脚本做了白盒渗透测试。​ ​ 这套方案上线后,我们没有遇到过一次租户数据串用的事故。​ ​ ​ ​ ​ 面试官: “权限变更的实时同步是怎么实现的?”​ ​ ​ 📔 答题思路:说明你如何兼顾响应速度与稳定性,提升实时性但不依赖单点。​ ​ 我们用了「WebSocket + 轮询兜底 + 缓存控制」三层方案,既保障实时性,又避免因连接问题导致功能不可用。​ WebSocket 推送:​ 用户权限变更后,服务端通过 WebSocket 发送变更事件。​ websocket.emit('permission_updated', { userId: '123', changes: [...], timestamp: Date.now() }); ​ 前端收到事件后:​ • 刷新权限缓存;​ • 判断当前页面权限是否变化,提示用户刷新或强制退出。​ ​ 轮询兜底机制:​ 每隔 5 分钟轮询后端权限接口,对比版本号,有更新就替换本地权限数据,防止推送丢失。​ ​ 权限缓存策略:​ • 权限结果缓存 30 分钟;​ • 如果某个操作被拒绝,会立即强制刷新;​ • 页面切换或激活时,也会自动检查权限是否过期。​ ​ 异常处理机制:​ 如果发现 WebSocket 断开,会自动降级为轮询模式。​ 权限同步失败时,我们采用“最小权限”策略,避免权限过宽造成风险。​ ​ 这套机制在复杂权限场景下稳定运行两年,几乎没有漏同步的情况。​ ​ ​ ​ ​ 面试官:“这个架构在大规模场景下的性能如何?”​ ​ 📔 答题思路:从服务端、前端、系统架构三层面讲清楚你做了哪些性能与扩展性设计。​ ​ 我们在用户量和租户量增长后,做了不少结构优化,确保性能可控、扩展性强。​ 服务端优化:​ • 权限计算做了缓存:​ 把用户、资源、动作作为三元组缓存结果,避免重复判断。​ const cacheKey = `${userId}-${resource}-${action}`; ​ • 数据库层面建了复合索引:​ tenant_id + user_id + resource 查询速度大幅提升。​ • 热点数据进 Redis:​ 高频资源或管理员权限用 Redis 做集中缓存,避免打爆主库。​ • 权限继承做了图结构改造:​ 不再递归查父角色,而是一次性 flatten 出用户所有权限。​ ​ 前端优化:​ • 权限判断用了 useMemo 缓存;​ • 页面首次加载时统一批量拉取所需权限,避免多个请求;​ • 路由切换时权限预先加载,提升页面跳转体验。​ ​ 压测结果:​ • 单个用户权限判断时间控制在 10ms 以内;​ • 实测支持 1000 用户并发权限校验;​ • 在 30 个租户、10 万用户并发情况下,平均接口响应时间 < 80ms。​ ​ ​ 扩展性设计:​ • 使用微前端架构,不同模块可独立部署,按需加载;​ • 权限系统作为独立微服务提供 REST API,可供其他系统复用;​ • 所有策略都通过配置驱动,支持后期动态扩展。​ ​ 这套架构我们已经在多个 SaaS 项目中复用过,验证了稳定性和灵活性。​ ​ ​ ​ ​ 💡 整体答题策略​ ​ 1. 画架构图:面试时可以在白板或在线工具上画出分层结构,帮助理解你的设计思路。​ 2. 举具体例子:比如“租户ID如何注入 SQL”、“权限缓存 key 的结构”,能说明你真的做过。​ 3. 考虑非功能需求:不要只谈功能逻辑,性能、安全、可维护性是考察重点。​ 4. 准备代码示例:权限服务、租户隔离、权限同步等核心逻辑要能写出伪代码或简化版实现。​ ​