【企业级编写笔记】企业级研发的原则
遵循企业级创建原则,养成良好的编程习惯,大幅提高项目的可扩展性与可维护性,让你的项目走得更远
核心开发原则
1. 核心的企业级开发原则:
高内聚低耦合:模块内部功能紧密相关(高内聚),模块之间依赖关系尽可能少(低耦合)。这有助于提高模块的独立性、可复用性和可维护性。
- 可扩展性 (Scalability):系统应能经过增加资源(如服务器、数据库实例)来处理不断增长的负载,而无需修改核心代码。这包括水平扩展(增加机器)和垂直扩展(增强单机性能)。
- 高可用性 (High Availability):环境应设计为在部分组件失效时仍能持续给予服务,通过冗余、故障转移、负载均衡等机制实现。
- 弹性 (Resilience):系统应能从故障中快速恢复,并优雅地降级服务,而不是完全崩溃。例如,使用熔断器、限流、重试机制等。
- 可维护性 (Maintainability):代码应易于理解、修改和测试。这包括清晰的命名、一致的编码风格、良好的文档和模块化设计。
- 安全性 (Security):从设计之初就考虑安全,遵循最小权限原则、深度防御、安全编码实践等。
- 可观测性 (Observability):系统应提供足够的日志、指标和链路追踪,以便在生产环境中监控、诊断和排查问题。
2. 编码实践原则
- DRY (Don’t Repeat Yourself):避免重复编写相同的代码逻辑。通过抽象、封装和复用组件来减少冗余。
- KISS (Keep It Simple, Stupid):设计和实现应尽可能容易,避免不必要的复杂性。简单的代码更容易理解、测试和维护。
- YAGNI (You Ain’t Gonna Need It):只实现当前需要的功能,避免过度设计和提前优化。这有助于减少不必要的复杂性和开发成本。
- 单一职责原则 (SRP):一个类或模块只负责一个功能或职责。这有助于提高模块的内聚性,降低耦合度。
- 开放/封闭原则 (OCP):软件实体(类、模块、函数等)应该对扩展开放,对修改封闭。这意味着在不修改现有代码的情况下,许可增加新机制。
- 依赖倒置原则 (DIP):高层模块不应该依赖低层模块,两者都应该依赖抽象。抽象不应该依赖于细节,细节应该依赖于抽象。这有助于解耦。
- 接口隔离原则 (ISP):客户端不应该被迫依赖它不使用的方法。一个类对另一个类的依赖应该建立在最小的接口上。
- 里氏替换原则 (LSP):子类型必须能够替换掉它们的基类型。这意味着在任何使用基类的地方,都可能运用其子类,而不会导致程序行为的改变。
- 错误处理:统一的、健壮的错误处理机制,避免裸露的异常,提供有意义的错误信息。
- 日志记录:规范的日志记录,包括不同级别的日志(DEBUG, INFO, WARN, ERROR),记录关键业务流程和异常信息,便于疑问追踪。
3. 数据管理原则
- 素材一致性:确保数据在分布式系统中的一致性,根据业务需求选择合适的强一致性或最终一致性模型。
- 数据备份与恢复:建立完善的数据备份策略和灾难恢复计划,确保数据安全和业务连续性。
- 数据加密:对敏感数据进行传输中加密(TLS/SSL)和静态加密(存储加密)。
- 数据审计:记录对敏感数据的访问和修改运行,以便进行安全审计和合规性检查。
4. 部署与运维原则
- 自动化部署:使用 CI/CD 流水线实现自动化构建、测试和部署,减少人工错误,提高部署效率。
- 容器化/虚拟化:利用 Docker、Kubernetes 等技术实现应用环境的标准化和隔离,简化部署和管理。
- 灰度发布/蓝绿部署:逐步发布新版本,降低发布风险,确保新版本稳定后再全面切换。
- 监控与告警:建立全面的监控体系,实时收集平台指标,并部署告警机制,及时发现和响应问题。
- 配置管理:外部化应用设置,依据调整中心(如 Spring Cloud Config, Nacos, Apollo)进行统一管理和动态更新。
5. 团队协作与流程原则
- 版本控制:使用 Git 等版本控制设备进行代码管理,遵循清晰的分支策略。
- 代码审查 (Code Review):通过代码审查提高代码质量,分享知识,发现潜在问题。
- 自动化测试:编写单元测试、集成测试、端到端测试,确保代码质量和作用正确性。
- 持续集成/持续交付 (CI/CD):频繁地将代码集成到主干,并自动化地进行测试和部署。
- 文档:保持技术文档、API 文档、设计文档的及时更新和清晰。
这些原则共同构成了企业级软件开发的基础,有助于构建高质量、高可靠、易于维护和扩展的系统。在实际开发中,需要根据具体项目和团队情况灵活应用和权衡这些原则。
企业级开发中“不应直接”处理的事项
对这些事项的全面总结:就是在企业级应用开发中,为了确保系统的安全性、可维护性、可扩展性和高性能,有许多事项不应直接进行处理或存储。以下
一、不应直接存储在数据库中的信息
1. 用户密码
问题:直接存储用户密码是严重的安全漏洞,一旦数据库泄露,所有用户密码将暴露。
正确做法:
- 加盐哈希:绝不能以明文形式存储。应使用加盐(Salt)的哈希算法(如 bcrypt、scrypt、Argon2)存储密码的哈希值。加盐可以有效防御彩虹表攻击,而强哈希算法可以防御暴力破解。
- 不可逆性:哈希是单向的,无法从哈希值逆推出原始密码。
2. 个人身份信息 (PII)
困难:身份证号、银行卡号、社保号等敏感个人信息直接存储,一旦泄露将造成严重后果,并可能违反内容隐私法规(如 GDPR、CCPA)。
正确做法:
- 加密存储:对 PII 进行加密存储,并严格控制解密密钥的访问权限。
- 脱敏处理:在非生产环境或不应该完整信息展示的场景下,对部分信息进行脱敏处理(如表现身份证号的后四位)。
- 访问控制:实施严格的基于角色的访问控制(RBAC),确保只有授权人员和环境才能访问这些数据。
3. 敏感业务数据
问题:商业机密、核心算法参数、高价值交易数据等直接存储,可能导致商业损失或竞争优势丧失。
正确做法:
- 加密存储:根据敏感程度,对数据进行加密存储。
- 细粒度访问控制:在应用层面搭建更细粒度的访问控制,限制对特定敏感数据的操作。
4. API 密钥/令牌
问题:用于访问第三方服务或内部服务的 API 密钥和令牌直接存储,可能导致未经授权的访问和滥用。
正确做法:
- 加密存储:加密存储这些凭证。
- 安全管理:使用专门的密钥管理服务(如 HashiCorp Vault、AWS KMS、Azure Key Vault)或配置中心来安全地管理和分发这些密钥。
- 定期轮换:定期更换密钥和令牌。
5. 大文件/二进制数据
问题:图片、视频、大型文档等大文件直接存储在关系型数据库中,会导致数据库膨胀、性能下降、备份恢复困难,且不适合流式访问。
正确做法:
- 对象存储/文件系统:将大文件存储在专门的对象存储服务(如 AWS S3、Azure Blob Storage、MinIO)或分布式文件系统(如 HDFS、Ceph)中。
- 数据库存储元数据:数据库中只存储文件的元数据(如文件名、大小、类型)和访问路径(URL)。
6. 日志数据
挑战:生产环境产生的大量日志直接写入业务数据库,会严重影响数据库性能和存储空间,且不利于日志的集中管理和分析。
正确做法:
- 专用日志系统:使用专门的日志收集、存储和分析系统(如 ELK Stack (Elasticsearch, Logstash, Kibana)、Splunk、Grafana Loki)。
- 异步写入:日志通常通过异步方式写入,避免阻塞业务线程。
7. 缓存数据
问题:临时性、高频访问但可重建的数据长期存储在关系型数据库中,会增加数据库负载,降低读取速度。
正确做法:
- 内存缓存:采用内存缓存环境(如 Redis、Memcached)来存储这些数据,提高读取速度并减轻数据库压力。
- 缓存策略:实施合适的缓存淘汰策略和数据同步机制。
8. 会话状态 (Session State)
问题:Web 应用的会话状态存储在单体应用的内存中,不利于应用的水平扩展和高可用性。
正确做法:
- 分布式会话存储:采用分布式会话存储(如 Redis、Spring Session with JDBC/Redis)来管理会话,支持多实例部署和故障转移。
二、不应直接硬编码在代码中的信息
1. 配置参数
问题:数据库连接字符串、API 密钥、第三方服务凭证、服务端口、环境特定路径等直接硬编码在代码中,导致环境切换困难,且敏感信息容易泄露。
正确做法:
- 外部化配置:通过配置文件(如
application.properties
/application.yml
)、环境变量、命令行参数或配置中心(如 Spring Cloud Config、Consul、Nacos)进行管理。 - 分环境调整:为不同环境(开发、测试、生产)提供不同的配置文件。
2. 业务常量/魔法字符串
障碍:例如状态码、错误信息、业务规则中的特定值等直接使用字符串或数字,降低代码可读性、可维护性,且容易出错。
正确做法:
- 枚举/常量类:定义为枚举(Enum)、常量类或通过配置管理,提高代码的可读性和可维护性。
3. 敏感算法参数
问题:加密算法的密钥、盐值等直接硬编码,存在严重安全风险。
正确做法:
- 安全加载:通过安全的方式加载和管理,例如从密钥管理服务中获取。
三、不应直接暴露给外部的信息
1. 内部 API 接口/微服务接口
问题:后端服务接口直接暴露给前端或外部调用者,缺乏统一管理、安全防护和流量控制。
正确做法:
- API 网关:通过 API 网关(API Gateway)进行统一管理、认证、授权、限流、熔断和路由,隐藏后端服务细节,提供统一的对外接口。
2. 敏感错误信息
疑问:生产环境的错误信息直接将堆栈跟踪或内部实现细节返回给用户,可能泄露系统内部结构和敏感信息,给攻击者提供便利。
正确做法:
- 友好提示:向用户返回友好的、通用的错误提示信息。
- 详细日志:将详细的错误信息(包括堆栈跟踪)记录到日志系统,供开发和运维人员排查问题。
3. 服务器/系统信息
困难:HTTP 响应头中包含服务器类型、版本号等敏感信息,可能被攻击者利用进行针对性攻击。
正确做法:
- 信息隐藏:调整 Web 服务器或应用服务器,移除或修改这些敏感信息。
四、不应直接进行的操作
1. 直接操作数据库
问题:在业务逻辑层直接拼接 SQL 语句,容易导致 SQL 注入漏洞,且代码可读性和可维护性差。
正确做法:
- ORM 框架/JDBC 模板:运用 ORM 框架(如 Hibernate、MyBatis)或 JDBC 模板,通过参数绑定等方式防止 SQL 注入,并提高开发效率。
2. 直接处理异常
问题:在每个方法中都进行重复的 try-catch
异常处理,导致代码冗余,且不利于统一的错误处理和日志记录。
正确做法:
- 统一异常处理:使用统一的异常处理机制(如 Spring 的
@ControllerAdvice
、全局异常过滤器),集中捕获、处理和记录异常,返回统一的错误响应格式。
3. 直接进行耗时操作
问题:在 Web 请求处理线程中直接进行资料 I/O、网络请求、麻烦计算等耗时操作,会导致请求响应慢,阻塞线程,降低系统吞吐量。
正确做法:
- 异步处理:使用异步编程(如 Spring 的
@Async
)、消息队列(如 Kafka、RabbitMQ)或单独的批处理服务来处理耗时操作,避免阻塞主线程。
4. 直接管理事务
问题:手动管理数据库事务的开启、提交和回滚,容易出错,且代码艰难。
正确做法:
- 声明式事务管理:使用框架提供的声明式事务管理(如 Spring 的
@Transactional
),通过注解或 XML 配置来管理事务,确保数据一致性。
五、企业级开发中的集中管理和封装实践
为了更好地遵循上述“不应直接”的原则,企业级开发应注重以下方面的集中管理和封装:
- 统一认证授权:使用 Spring Security、OAuth2/JWT 等标准,集中管理用户身份验证和权限控制,确保所有模块遵循统一的安全策略。
- 统一日志管理:通过 Logback/Log4j2 配合日志收集系统(如 ELK Stack),实现日志的集中收集、存储、查询和分析,便于问题排查和系统监控。
- 统一配置中心:使用 Spring Cloud Config、Nacos、Consul 等,搭建应用调整的集中管理和动态刷新,提高配置的灵活性和可维护性。
- 统一异常处理:通过全局异常处理器,集中捕获和处理所有未捕获的异常,返回统一的错误格式,提升用户体验和系统健壮性。
- 统一数据访问层:封装数据访问对象(DAO)或 Repository 层,将数据操作与业务逻辑分离,提高代码的模块化和可测试性。
- 服务治理:在微服务架构中,通过注册中心、设置中心、熔断器、限流器等组件,构建服务的发现、调用、监控和保护,确保框架的稳定性和弹性。
- 安全模块封装:将认证、授权、密码处理、数据加密等安全相关逻辑封装在独立的模块中,确保其可复用性和安全性,并便于安全审计。
遵循这些原则,并凭借有效的集中管理和封装,允许构建出更健壮、安全、可维护和高性能的企业级应用系统。
假设有什么缺漏,欢迎补充
假如这篇文章对你有帮助请点个三连帮助一下,谢谢!