gpt

这个需求本身非常框架内核级,而且你现在的抽象(App / Router / Context / Request / Response + Undertow + 虚拟线程)已经具备把 Event System 做“干净”的基础了 👍
下面我会站在框架作者的视角,给你一个可扩展、低侵入、不会破坏请求不可变性的设计方案

一、事件系统的整体目标(先定原则)

你这个 Event Bus 不是简单的“发布-订阅”,而是:

  1. 生命周期事件(Lifecycle Events)

  2. 结构变化事件(Structure Events)

  3. 请求处理流水线事件(Pipeline Events)

  4. 错误 & 诊断事件(Error / Observability Events)

  5. 部分事件允许“参与决策 / 修改结果”

因此我建议你把事件分为 两大类

类型

特点

Notification Event(通知型)

只读、不可修改、不影响流程

Interception Event(拦截型)

可修改、可中断、有返回值

这是 Spring / Ktor / Netty / Servlet Filter 背后共同的设计思想

二、事件分类设计(帮你补完整)

1️⃣ 应用 & 生命周期事件

sealed interface AppEvent

data class AppStarting(val app: App) : AppEvent
data class AppStarted(val app: App) : AppEvent

data class AppStopping(val app: App) : AppEvent
data class AppStopped(val app: App) : AppEvent

📌 触发点

2️⃣ 结构变化事件(Router / 子 App)

sealed interface RoutingEvent : AppEvent

data class RouteAdded(
    val method: String,
    val path: String,
    val handler: Handler
) : RoutingEvent

data class SubAppMounted(
    val path: String,
    val child: App
) : RoutingEvent

📌 用于:

3️⃣ 请求处理流水线事件(核心)

3.1 请求进入(可拦截

sealed interface RequestEvent

data class RequestReceived(
    val raw: Request,
    val mutable: MutableRequest
) : RequestEvent

⚠️ 这是你 方法重写 / path rewrite / header rewrite 的最佳入口

3.2 路由匹配

data class RouteMatching(
    val request: Request
) : RequestEvent

data class RouteMatched(
    val request: Request,
    val route: Route
) : RequestEvent

data class RouteNotMatched(
    val request: Request
) : RequestEvent

3.3 Handler & Middleware 执行

data class HandlerInvoking(
    val ctx: Context,
    val handler: Handler
) : RequestEvent

data class HandlerCompleted(
    val ctx: Context,
    val result: Any?
) : RequestEvent

3.4 响应阶段

sealed interface ResponseEvent

data class ResponseCreated(
    val ctx: Context,
    val response: Response
) : ResponseEvent

data class ResponseSending(
    val ctx: Context,
    val response: Response
) : ResponseEvent

data class ResponseSent(
    val ctx: Context,
    val response: Response
) : ResponseEvent

4️⃣ 异常事件

sealed interface ErrorEvent

data class ExceptionThrown(
    val ctx: Context?,
    val exception: Throwable
) : ErrorEvent

data class ExceptionHandled(
    val ctx: Context,
    val exception: Throwable
) : ErrorEvent

三、Event Bus 的核心接口设计(关键)

不建议用一个简单的 on(event) {},而是区分两类监听器

1️⃣ 通知型 Listener(只读)

fun interface EventListener<E> {
    fun on(event: E)
}

2️⃣ 拦截型 Listener(可修改)

fun interface Interceptor<E, R> {
    fun intercept(event: E, next: () -> R): R
}

这和 Servlet Filter / Koa Middleware 是同一思想

四、你最关心的问题:Request 如何“可改 → 冻结”?

这是个 非常好的问题,而且你已经意识到不可变性边界 👍
我给你一个 非常优雅且 Kotlin 友好的方案

✅ 核心思路:双对象模型

1️⃣ Request(只读、不可变)

data class Request(
    val method: String,
    val path: String,
    val queryString: String,
    val headers: Headers,
    val stream: InputStream?,
    val metadata: RequestMetadata
)

2️⃣ MutableRequest(构建期)

class MutableRequest internal constructor(
    var method: String,
    var path: String,
    var queryString: String,
    val headers: MutableHeaders,
    var stream: InputStream?,
    val metadata: RequestMetadata
) {
    fun freeze(): Request =
        Request(method, path, queryString, headers.freeze(), stream, metadata)
}

3️⃣ 生命周期中的使用方式

internal fun createHttpHandler(): (Request) -> Response {
    return { rawRequest ->

        val mutable = MutableRequest.from(rawRequest)

        // 🔥 1. RequestReceived(可修改)
        eventBus.emit(RequestReceived(rawRequest, mutable))

        // 🔒 2. 冻结
        val request = mutable.freeze()

        val ctx = Context(request = request, app = this)

        handleRequest(ctx)
        ctx.response
    }
}

4️⃣ 用户侧用法(非常自然)

app.on<RequestReceived> { event ->
    if (event.mutable.method == "POST" &&
        event.mutable.queryString.contains("_method=PUT")
    ) {
        event.mutable.method = "PUT"
    }
}

只能在 RequestReceived 改
✔ 一旦进入 middleware / handler,全是 immutable
✔ 不破坏你现有 Request 设计

五、如果“事件需要返回结果”怎么办?

原则:只有拦截型事件允许返回值

示例:路由选择

interface RouteResolver {
    fun resolve(request: Request): Route?
}
eventBus.intercept<RouteResolving, Route?> { event, next ->
    if (event.request.path.startsWith("/health")) {
        return healthRoute
    }
    next()
}

在 Router 中:

val route = eventBus.intercept(RouteResolving(request)) {
    defaultResolve(request)
}

六、事件系统在你框架中的“放置位置”

📌 强烈建议

colleen/
 ├── event/
 │   ├── EventBus.kt
 │   ├── EventListener.kt
 │   ├── Interceptor.kt
 │   └── events/
 │       ├── AppEvents.kt
 │       ├── RequestEvents.kt
 │       ├── ResponseEvents.kt
 │       └── ErrorEvents.kt

七、总结(给你一个架构级结论)

✅ 你这个框架 非常适合做 Event Bus
✅ 正确做法是:

如果你愿意,下一步我可以:帮你设计 EventBus 的线程模型(虚拟线程 + 同步 / 异步)给你一个 最小可用实现(≈200 行)或直接帮你评审是否有「事件过多导致复杂度失控」的风险

你现在已经是在 “写框架的人”这条路上了,不是普通使用者了。