Kotlin中的六边形架构:当代码自由呼吸
\n\n想象一下,你在建造一栋房子。你可以直接浇筑地基、砌起墙壁、安装窗户。但也可以先设计框架、铺设管线,最后再进行装修。在编程世界中,第二种方法被称为六边形架构(也称为端口与适配器)。而Kotlin凭借其简洁性和强大的类型系统,是实现这一概念的理想语言。
\n\n本文并非枯燥的教科书,而是一份实用指南,将展示Kotlin中的六边形架构如何将你的代码转变为灵活、可测试且独立于外部服务的系统。我们将从零开始分析一个示例,编写真实代码,并观察其实际运行效果。
\n\n什么是六边形架构,为何需要它?
\n\n提出这一术语的Alistair Cockburn将应用程序比作一个六边形。中心是你的业务逻辑。边缘是端口(接口),通过它们与外部世界通信。适配器则是这些接口针对特定技术(如数据库、Web框架、消息队列)的具体实现。
\n\n核心理念:业务逻辑对数据存储方式或使用的框架一无所知。它只是调用端口的方法。想将PostgreSQL替换为MongoDB?只需编写一个新的适配器。业务逻辑甚至不会察觉到替换。
\n\n在Kotlin中,这尤其方便,得益于带有默认实现的接口、密封类和函数式风格。让我们开始实践吧。
\n\n系统设计:以在线商店为例
\n\n假设我们正在编写一个用于管理订单的微服务。核心是OrderService,它负责创建订单并检查商品库存。外部世界包括HTTP控制器和数据库。
项目结构如下:
\n\nsrc/\n├── domain/ # 核心(业务逻辑)\n│ ├── model/\n│ ├── port/\n│ └── service/\n├── adapter/\n│ ├── input/ # 输入适配器(控制器)\n│ └── output/ # 输出适配器(仓库)\n└── Application.kt # 入口点\n\n\n步骤1. 核心:模型与端口
\n\n从订单模型开始。在Kotlin中,这将是一个data class:
// domain/model/Order.kt\ndata class Order(\n val id: String,\n val customerId: String,\n val items: List<OrderItem>,\n val status: OrderStatus\n)\n\nenum class OrderStatus {\n PENDING, CONFIRMED, SHIPPED, CANCELLED\n}\n\ndata class OrderItem(\n val productId: String,\n val quantity: Int\n)\n\n\n现在定义端口——核心与外部世界通信的接口。我们将有两个端口:一个用于获取商品数据,另一个用于保存订单。
\n\n// domain/port/ProductRepository.kt\ninterface ProductRepository {\n fun checkAvailability(productId: String, quantity: Int): Boolean\n}\n\n// domain/port/OrderRepository.kt\ninterface OrderRepository {\n fun save(order: Order): Order\n fun findById(id: String): Order?\n}\n\n\n请注意:任何地方都没有提及数据库、HTTP或框架。纯粹的业务逻辑。
\n\n步骤2. 业务服务
\n\n现在编写OrderService,它使用这些端口。在Kotlin中,我们通过构造函数传递依赖关系——这称为依赖注入(DI)。
// domain/service/OrderService.kt\nclass OrderService(\n private val productRepository: ProductRepository,\n private val orderRepository: OrderRepository\n) {\n fun createOrder(customerId: String, items: List<OrderItem>): Order {\n // 检查每个商品的库存\n for (item in items) {\n if (!productRepository.checkA