整洁架构or整洁代码?或许需要一个整洁的API!
【引】你可能读过《clean architecture》一书, 也读过《clean code》,如果缺了些什么? 那可能就是 Clean API 了。本文译自“https://medium.com/perry-street-software-engineering/clean-api-architecture”。
在软件架构领域,网上讨论最广泛的架构之一是整洁架构(Clean Architecture)。它通过将项目划分为多个层级,实现关注点分离,从而提升代码的可维护性和可扩展性。
每一层都遵循单一职责原则,确保每个类只负责一部分逻辑,不仅使系统结构更清晰,也极大地方便了单元测试的编写与执行。
整洁架构的核心理念可以概括为:
依赖关系向内指向“业务核心”,外层可以依赖内层,但内层绝不可以反向依赖外层。
换句话说,没有哪一层可以看到比它更高层的细节。它们可以引用自己的子层,但从不允许跨层依赖或反向耦合。
这种设计思想不仅适用于整体系统架构,在具体场景如 API 开发中同样具有重要价值。那么,如何将 Clean Architecture 的理念应用到 API 端点的设计中?这就引出了“Clean API 架构”这一实践模式:
它将接口层、应用服务、领域逻辑和数据访问等模块清晰隔离,使 API 结构更加清晰、易于测试和长期演进。
1. 框架:从请求入口到架构分层
在现代 Web 系统中,任何一个 API 请求通常都需要经过多个层级的处理——从负载均衡器、Web 服务器,到应用服务器,最终由具体的 API 或 Web 框架将请求路由到正确的代码路径。目前主流的开发框架如 Rails、Django 和 Spring Boot 等都提供了丰富的文档支持和成熟的生态体系,是大多数开发者首选的技术栈。
然而,当请求真正进入框架并开始业务逻辑处理后,不同系统的设计路径往往开始显著分化。
以 Rails 为例,其采用经典的 MVC(Model-View-Controller)架构,在小型项目中表现优异,结构清晰且上手成本低。但对于大型、高可用性的 API 系统而言,这种模式逐渐显露出局限性——控制器和模型容易膨胀为臃肿的“上帝类”,违背了单一职责原则,导致维护困难、测试复杂。
正因如此,在框架层级之下,我们所构建的整个系统设计更加注重解耦与可扩展性,并深受 Clean Architecture 的启发。这套设计理念强调业务逻辑应独立于外部依赖(如数据库、UI、框架等),从而提升系统的可测试性和长期可维护性。
在本系列后续内容中,我们将深入剖析我们是如何按照这一思想来组织各层级代码的。
在每一层中,我们会定义一个或多个单一职责的支持类,它们只服务于当前层级,不引用上下层的具体实现。这种严格隔离不仅有助于代码复用,也有效避免了层级混乱和过度耦合的问题。
此外,虽然我们在架构中使用了诸如 AWS EC2、SQS、RDS 和 ElastiCache 等云服务,但这些服务在我们的设计中被作为框架层的辅助工具类存在,而非核心业务逻辑的一部分。正如 Clean Architecture 所强调的那样:业务规则不应依赖于基础设施或数据存储方式,而应保持完全的独立。这也确保了我们的系统具备更强的可移植性与灵活性。
2. 接口适配器层:连接外部世界与核心逻辑的桥梁
当一个请求穿越框架层,进入系统内部时,接口适配器层便开始发挥作用。这一层的核心职责是将外部输入转化为内部可理解的数据结构,并将应用逻辑的执行结果以合适的格式返回给调用者。
在这一过程中,**控制器(Controller)**扮演着协调者的角色。它首先通过 Request
对象提取请求参数,验证其语法格式,并完成用户身份认证等前置操作。随后,控制器实例化相应的业务类,驱动数据在不同层级之间的流转,从而启动真正的应用逻辑处理流程。
值得注意的是,控制器并不是接口适配器层中唯一负责业务流程的对象。我们还引入了 Jobs(任务),用于处理异步队列相关的操作——这部分内容将在后续章节中详细展开。
为了确保系统的清晰分层与职责分离,控制器依赖于多个辅助类:
-
Validators(验证器)
负责检查输入数据的合法性,确保进入系统的信息符合预期格式;
-
Presenters(展示器)
专注于输出数据的格式化处理,为上层逻辑提供统一的数据视图;
-
Response(响应)对象
则承担最终输出的封装工作,能够将数据转换为 JSON、HAML 或其他客户端可识别的格式返回。
此外,系统中还包含一种特殊的适配器——套接字中继类(Socket Relay),它通过 WebSocket 等通信通道,实时将状态变更推送给客户端,实现双向通信能力。
Request 类则是一个类型化的数据结构,聚合了当前请求所需的所有信息。与传统 HTTP 请求(通常是以键值对形式存在的 CGI 风格请求)不同,这种设计提供了更强的类型安全性和结构清晰性。
Response 类的功能类似于 Rails 中的渲染器,但它更加灵活,支持多种输出格式,如 HAML、JSON 或自定义类型,便于构建多端兼容的 API 响应。
最后,参数提取器(Parameter Extractor) 从原始的 params 散列中提取数据,并将其转换为正确的类型,如整数、浮点数或字符串,为后续逻辑提供强类型的输入保障。
整体而言,接口适配器层作为系统的“翻译官”,在外部请求与内部逻辑之间建立起高效、清晰的桥梁,是实现 Clean Architecture 分层思想的重要一环。
3. 应用逻辑层:业务流转的核心引擎
在 Clean API 架构中,应用逻辑层是整个系统真正开始处理业务需求的地方。它承接来自接口适配器层的请求,并协调数据验证、权限控制、外部调用以及最终的业务执行。
对于 GET 请求这类读取型端点,请求一旦进入该层,首先由服务类(Service)进行处理。服务对象负责确保输入参数的有效性,验证用户是否有权限访问目标资源,并通过 Repo(用于数据库操作) 或 Adapter(用于外部 API 调用) 从实体逻辑层获取所需数据。
在数据获取完成后,服务对象将结果封装为一个由 Result
对象返回。这种设计不仅统一了成功与失败的返回结构,也便于上层(如控制器)根据结果类型做出相应的响应决策。
而对于 POST、PUT 和 DELETE 等写入型请求,应用逻辑的处理流程类似,但引入了异步机制以提升性能和可靠性。服务对象仍然负责验证输入、授权用户,并准备写入所需的数据。不同之处在于,这些变更操作会被包装并提交到我们的**任务队列(基于 Amazon SQS)中排队,交由后台的作业(Job)**或异步服务来执行真正的数据写入操作。这种方式既减轻了主流程的压力,也增强了系统的容错能力和可扩展性。
此外,作业还承担着触发副作用的职责。例如,在数据持久化完成之后,作业可以通过 Relay 模块向客户端发送 WebSocket 消息,实时通知状态变更,实现前后端之间的即时反馈。
值得一提的是,在本架构中,Service 类还会组合一组专门的 Validator 类,对请求内容进行语义级别的验证。这意味着我们在系统中构建了双层验证机制:
-
语法验证
发生在请求层,确保传入的数据格式正确;
-
语义验证
则在应用逻辑层进行,确保数据在业务规则下是合理且合法的。
这种分层验证策略显著提升了系统的健壮性,避免了无效或非法数据对核心业务逻辑造成干扰,同时也使代码更具可测试性和可维护性。
4. 实体逻辑层:业务规则与数据交互的核心
实体逻辑层(Entity Logic Layer) 是系统中最具通用性和复用价值的部分。它不仅服务于当前 API 端点,也为其他多个接口和业务流程提供基础能力支撑。这一层承载了系统的核心业务规则以及与外部存储系统的交互逻辑。
在这一层级中,我们实现对持久化数据库(如 MySQL 或 PostgreSQL)的访问,封装了数据的读取、写入和转换逻辑;同时,Adapter 类 则负责对接各类外部服务 API,例如 AWS 提供的 S3(对象存储)、ElastiCache(缓存服务)等,使得系统能够灵活集成多种基础设施资源。
与上层(如应用逻辑层)中为特定端点定制的服务类不同,实体逻辑层中的类设计强调高内聚、低耦合与广泛复用性。它们通常不依赖于具体的请求或业务场景,而是围绕领域模型构建稳定的数据访问和业务处理能力。
简而言之,实体逻辑层是整个 Clean API 架构中最接近“不变”的部分——它屏蔽了外部变化的影响,确保系统核心逻辑稳定可靠,同时也为上层模块提供了统一、可测试、可替换的数据交互接口。
5. 数据层:存储抽象与适配的关键一环
数据层(Data Layer) 是整个 Clean API 架构中最底层的一环,其核心职责是为上层模块提供统一的数据访问接口,并屏蔽具体存储实现的细节。理想情况下,这一层应保持高度简洁和可替换,专注于连接数据库、缓存、文件系统或其他持久化机制。
为了实现跨平台一致性,我们在不同技术栈中(例如 Android 或 iOS 开发)也为数据存储层建立了统一接口。通过依赖注入(Dependency Injection) 技术,我们可以在测试时轻松替换真实的数据源为内存中的模拟实现(Mock)。例如,在本地运行单元测试时,可以使用基于 SQLite 的内存数据库代替实际的文件系统或远程服务,从而提高测试效率并减少外部依赖的影响。
在 Web 服务器环境中,我们通常将云服务(如 Redis、Memcached、MySQL 等)抽象为单例对象,并根据部署环境动态指向不同的实际资源。例如,在开发阶段,这些服务可以指向本地运行的 Docker 容器;而在生产环境中,则连接真实的云服务实例。
支撑数据层的各类存储系统——如 MySQL 和 Postgres——通常以进程级别的单例形式存在,并通过依赖注入或配置管理进行初始化和替换。像 ActiveRecord 这样的 ORM 会维护自己的连接池,而 Redis 和 Memcached 等服务也需要类似的全局访问控制机制来管理连接资源。
对于基于 HTTP 的无状态服务(如 S3、DynamoDB 等),我们通常采用模拟双(Instance Doubles)或覆盖连接参数的方式来隔离外部环境。这使得测试过程更加可控,同时也能保证代码逻辑在不同环境下的一致性。
总之,数据层不仅是系统与外部世界交互的桥梁,更是实现可测试性、可维护性和可扩展性的关键所在。通过良好的抽象设计与灵活的注入策略,它确保了我们的业务逻辑不受底层存储细节的牵制,真正做到“一次编写,多环境运行”。
6. 这是否过度设计?让我们通过一个简单的示例来探讨
为了更好地理解各层架构的实际应用,我们来看一个最基础的 API 示例:将文件添加到收藏夹。即使在这样一个看似简单的操作中,每一层的设计理念依然得到了体现。
假设我们要创建一个允许用户将某个文件添加到其收藏夹的功能。在这个过程中,尽管表面上看只需要一个简单的数据库操作,但实际上,这个功能隐式地依赖于我们之前定义的每一层架构。
请求(Request)
请求参数直接从 HTTP 请求中提取,包含 target_id
(目标文件的ID)和 creator_id
(执行该操作的用户ID)。这些未经处理的原始参数构成了一个隐式的请求对象。
Params: { "target_id": 123, "creator_id": 456 }
控制器(Controller)
由于此场景下没有复杂的验证或表示逻辑需求,因此无需专门编写控制器代码。这意味着我们可以跳过这一步骤,直接进入服务层处理业务逻辑。
服务(Service)
服务层在此处承担了主要职责,它接收来自请求的 target_id
和 creator_id
,并查找或创建相应的领域对象 Favorite
。这一过程确保了输入的有效性和用户的授权状态,并协调后续的数据处理步骤。
实体逻辑(Entity Logic)
实体逻辑层负责与持久化存储交互,这里使用了一个特殊的 ActiveRecord 方法 first_or_create
来检查是否存在符合条件的记录,若不存在则创建新记录。这种方法不仅简化了数据访问逻辑,还保证了数据的一致性。
数据(Data)
在数据层,Favorite
模型充当了与底层数据库交互的角色,提供了对特定对象的操作接口。它封装了所有与数据库相关的细节,如连接管理、查询构建等,使得上层代码可以专注于业务逻辑而非技术实现。
通过这个简单的例子可以看出,即使是看似微不足道的功能,Clean API 的分层设计也能够提供清晰的结构划分,确保每个部分专注于自己的职责。这样的设计虽然初看起来可能显得有些复杂,但它极大地提高了代码的可维护性、测试性和扩展性。随着系统规模的增长,这种架构的优势将会更加明显。
网络安全学习路线&学习资源
网络安全的知识多而杂,怎么科学合理安排?
下面给大家总结了一套适用于网安零基础的学习路线,应届生和转行人员都适用,学完保底6k!就算你底子差,如果能趁着网安良好的发展势头不断学习,日后跳槽大厂、拿到百万年薪也不是不可能!
初级网工
1、网络安全理论知识(2天)
①了解行业相关背景,前景,确定发展方向。
②学习网络安全相关法律法规。
③网络安全运营的概念。
④等保简介、等保规定、流程和规范。(非常重要)
2、渗透测试基础(一周)
①渗透测试的流程、分类、标准
②信息收集技术:主动/被动信息搜集、Nmap工具、Google Hacking
③漏洞扫描、漏洞利用、原理,利用方法、工具(MSF)、绕过IDS和反病毒侦察
④主机攻防演练:MS17-010、MS08-067、MS10-046、MS12-20等
3、操作系统基础(一周)
①Windows系统常见功能和命令
②Kali Linux系统常见功能和命令
③操作系统安全(系统入侵排查/系统加固基础)
4、计算机网络基础(一周)
①计算机网络基础、协议和架构
②网络通信原理、OSI模型、数据转发流程
③常见协议解析(HTTP、TCP/IP、ARP等)
④网络攻击技术与网络安全防御技术
⑤Web漏洞原理与防御:主动/被动攻击、DDOS攻击、CVE漏洞复现
5、数据库基础操作(2天)
①数据库基础
②SQL语言基础
③数据库安全加固
6、Web渗透(1周)
①HTML、CSS和JavaScript简介
②OWASP Top10
③Web漏洞扫描工具
④Web渗透工具:Nmap、BurpSuite、SQLMap、其他(菜刀、漏扫等)
恭喜你,如果学到这里,你基本可以从事一份网络安全相关的工作,比如渗透测试、Web 渗透、安全服务、安全分析等岗位;如果等保模块学的好,还可以从事等保工程师。薪资区间6k-15k
到此为止,大概1个月的时间。你已经成为了一名“脚本小子”。那么你还想往下探索吗?
7、脚本编程(初级/中级/高级)
在网络安全领域。是否具备编程能力是“脚本小子”和真正黑客的本质区别。在实际的渗透测试过程中,面对复杂多变的网络环境,当常用工具不能满足实际需求的时候,往往需要对现有工具进行扩展,或者编写符合我们要求的工具、自动化脚本,这个时候就需要具备一定的编程能力。在分秒必争的CTF竞赛中,想要高效地使用自制的脚本工具来实现各种目的,更是需要拥有编程能力.
零基础入门,建议选择脚本语言Python/PHP/Go/Java中的一种,对常用库进行编程学习; 搭建开发环境和选择IDE,PHP环境推荐Wamp和XAMPP, IDE强烈推荐Sublime; ·Python编程学习,学习内容包含:语法、正则、文件、 网络、多线程等常用库,推荐《Python核心编程》,不要看完; ·用Python编写漏洞的exp,然后写一个简单的网络爬虫; ·PHP基本语法学习并书写一个简单的博客系统; 熟悉MVC架构,并试着学习一个PHP框架或者Python框架 (可选); ·了解Bootstrap的布局或者CSS。
8、超级网工
这部分内容对零基础的同学来说还比较遥远,就不展开细说了,贴一个大概的路线。感兴趣的童鞋可以研究一下,不懂得地方可以【点这里】加我耗油,跟我学习交流一下。
网络安全工程师企业级学习路线
如图片过大被平台压缩导致看不清的话,可以【点这里】加我耗油发给你,大家也可以一起学习交流一下。
一些我自己买的、其他平台白嫖不到的视频教程:
需要的话可以扫描下方卡片加我耗油发给你(都是无偿分享的),大家也可以一起学习交流一下。
结语
网络安全产业就像一个江湖,各色人等聚集。相对于欧美国家基础扎实(懂加密、会防护、能挖洞、擅工程)的众多名门正派,我国的人才更多的属于旁门左道(很多白帽子可能会不服气),因此在未来的人才培养和建设上,需要调整结构,鼓励更多的人去做“正向”的、结合“业务”与“数据”、“自动化”的“体系、建设”,才能解人才之渴,真正的为社会全面互联网化提供安全保障。