> For the complete documentation index, see [llms.txt](https://docs.zongsoft.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.zongsoft.com/overview/pluginization/host-independent-business.md).

# 让业务能力跨宿主复用

把业务动作留在服务里，让控制器、命令和后台入口共用同一套规则。

论坛的审核最初只通过 Web 界面完成：版主打开页面点击“批准”。如果后续要把审核能力交给批量命令、定时任务或合作系统，最值得复用的不是页面，也不是控制器，而是那套“什么条件下允许批准、批准后更新什么”的业务动作。

服务承载业务动作，入口负责把外部输入翻译成业务调用、再把结果翻译回各自的输出方式。Zongsoft 把宿主、插件装配和业务服务分开，正是为了让同一条业务规则能够被不同入口复用。关键前提是：**业务服务不能绑定某个入口的输入输出方式，也不该反向依赖某个入口**。

![同一业务能力可以由多种入口调用](https://847714710-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FdTV8LtunzSisrDXg3zNv%2Fuploads%2Fgit-blob-ec95d0b370ccaf9cbc070d8cb667422d3597d5d4%2Fzongsoft-plugin-host-independent-business.png?alt=media)

*各入口先补齐身份、业务范围与配置，再调用同一个审核服务；图中批量命令与后台入口属于待设计的使用方式。中间栏表示共同的设计责任，不是框架新增的网关组件。*

## 从一次真实调用读出职责

[ThreadController](https://github.com/Zongsoft/discussions/blob/main/src/api/Controllers/ThreadController.cs) 的审核动作调用 [ThreadService.Approve](https://github.com/Zongsoft/discussions/blob/main/src/Services/ThreadService.cs)，再把布尔结果映射为 HTTP 204 或 404。控制器薄而直白：它不重新实现“版主才能批准”“未审核才能批准”，只决定“批准成功返回 204，否则返回 404”。相关实现见[请求与数据服务接口](/framework/web/data-services.md)。

这次分离留下一个值得注意的结果约定：`Approve` 返回 `false` 表示没有发生符合条件的更新，它不区分主题不存在、已经批准或当前用户不是版主。如果新的命令入口需要向操作者展示更细的原因，应当评估扩展业务结果契约，并注意权限信息暴露的范围；命令不应自行猜测失败原因。

## 依赖方向决定复用是否成立

让入口和服务可以拆开使用的前提是依赖方向一致：业务库不引用 Web 库，Web 库引用业务库。Discussions 的领域程序集保持自身依赖边界，[Discussions.Web](https://github.com/Zongsoft/discussions/tree/main/src/api) 只做适配。业务服务返回领域结果，不产生 HTTP 响应；终端命令也不该为了让服务执行而模拟一次 HTTP 请求。

依赖方向同样约束元数据：领域能力需要的服务、映射、选项随领域插件交付；Web 入口需要的能力（控制器、认证中间件）依赖 Web 宿主。把 HTTP 语义写进领域服务，就等于要求所有复用入口都提供 HTTP 环境。

{% hint style="info" %}
💡 分层不需要为每个方法机械地增加一层接口。当前控制器直接使用具体数据服务；当确有跨模块消费者需要稳定契约、或确有多个实现需要替换时，再引入接口并明确其所有者。可维护性来自职责清楚，也来自避免无意义的转发层。
{% endhint %}

## 新入口要自己补齐调用上下文

从 Web 入口进入服务时，应用已经建立了认证主体、请求环境和已装配的插件。换成命令或后台入口，这些条件不会自动出现——尤其是“当前调用者是谁”这件事。

| 需要明确的内容 | Web 入口中的来源 | 新入口需要安排的事情              |
| ------- | ---------- | ----------------------- |
| 调用身份    | 认证与授权流程    | 建立受信任的执行身份及对应权限，后台不等于版主 |
| 业务范围    | 请求参数及服务约束  | 明确站点、目标资源与允许处理的范围       |
| 配置与服务   | 插件宿主装配     | 部署领域依赖、配置连接并验证服务解析      |
| 完成与失败   | HTTP 响应    | 约定命令结果、任务记录与失败处理        |
| 中止与重试   | 请求生命周期     | 采用入口支持的取消方式，并验证重复执行语义   |

表中的“需要明确的内容”是设计责任，不表示当前所有服务方法都接收统一上下文参数。实现新入口时，应按现有 API 显式传参或建立执行上下文；需要补充契约时，把兼容性作为变更的一部分。

## 按生命周期放置状态与工作

“审核状态”可能指页面上选中的主题、批处理已完成的条目、或数据库中正式批准的结果。它们分别跟随交互、任务执行与持久数据的生命周期，不应都塞进同一个共享模块实例。

持续监听消息的入口可以用[工作器](https://github.com/Zongsoft/framework/blob/main/Zongsoft.Core/src/Components/IWorker.cs)管理启动与停止，再在每次收到消息时调用服务；应用启动阶段的装配使用[初始化器](https://github.com/Zongsoft/framework/blob/main/Zongsoft.Core/src/Services/IApplicationInitializer.cs)。不要把长期任务放进服务构造函数来替代生命周期管理。共享服务也不要保存“这一次审核”的主题编号或用户身份，调用数据应保持在明确的执行范围内，详见[服务定位与所有权](/framework/core/services/locating.md)。

拟新增的批量审核入口若需要断点续办，应设计持久任务记录保存进度，并在恢复时重新核验权限与目标状态；仅保存在进程字段里的进度会随进程退出而丢失，恢复内存快照也不能撤销已经提交的审核结果。

## 用两类验证确认复用成立

第一类验证直接针对业务结果：非版主被拒绝、重复批准符合约定、正文批准状态一致。只要提供必要的配置、身份与数据依赖，这类验证应能调用业务服务完成，不需要为验证规则而启动完整 HTTP 管线。

第二类验证针对入口本身：控制器如何映射结果、命令如何取得身份、后台任务中断后如何恢复。新入口可以共用第一类验证，但仍需要自己的适配验证。这样才分得清一次变化是改变了业务规则，还是只改变了业务的使用方式。

HTTP 入口遵守 [REST API 设计规范](https://github.com/Zongsoft/Guidelines/blob/main/zongsoft.rest-api.guidelines.md)，C# 实现遵守 [C# 编码规范](https://github.com/Zongsoft/Guidelines/blob/main/zongsoft.csharp.guidelines.md)。接下来阅读[把扩展点设计成协作契约](/overview/pluginization/extension-contracts.md)，判断新能力应该通过服务调用、扩展集合还是事件协作。

## 延伸阅读

* 选择承载业务能力的宿主：终端、后台服务与 Web 的差异见[宿主概览](/hosting/hosting.md)。
* 模块服务如何解析与回退见[插件应用模型](/framework/plugins/application-model.md)。
* 服务所有权与作用域见[服务定位与所有权](/framework/core/services/locating.md)。
* 需要多入口共享的业务应先划清边界，见[从业务变化确定模块边界](/overview/pluginization/business-boundaries.md)。


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.zongsoft.com/overview/pluginization/host-independent-business.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
