6.架构设计场景题
查看飞书原文 ↗3,761 字符
开场话术
"这个功能涉及到多个业务模块,所以架构设计是核心考虑点。"
"当时我们考虑了扩展性和维护性,设计了一套可复用的架构。"
标准答题示例
我们要做一个多租户的权限管理系统,需要支持不同客户的个性化配置。
架构上我采用了分层设计:
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<string, any>;
}
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 : <NoPermission />;
};此外,我们还通过事件机制让服务层可以通知上层做界面刷新,保持了良好的解耦性。
面试官: “多租户的数据隔离是怎么保证的?”
📔
答题思路:从数据库、应用、前端、测试四个层级说明如何防止数据串租。
多租户场景下,我们用到了强制型隔离机制,分为四层:
数据库层面:
•
所有数据表设计都加了 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.
准备代码示例:权限服务、租户隔离、权限同步等核心逻辑要能写出伪代码或简化版实现。