codeigniter 应采用分层架构与显式依赖:业务逻辑移至独立 service 类(如 orderservice),用 repository 替代传统 model,事务由 service 统一控制,避免状态污染与嵌套事务,并引入状态机管理订单等复杂状态流转。

CodeIgniter 本身不强制任何设计模式,但面对复杂业务逻辑时,硬塞进 Controller 或 Model 会导致难以测试、复用和维护。真正的解法不是“怎么在 CI 里写更多代码”,而是把职责切出去——用分层 + 显式依赖的方式组织逻辑。
把业务逻辑从 Controller 移到独立 Service 类
CI 的 Controller 天然适合做请求调度和响应组装,不适合写订单核验、库存扣减、优惠券叠加计算这类多步骤、有状态、需事务控制的逻辑。直接在 Controller 里调 $this->load->model() 再拼 SQL,很快就会变成“上帝类”。
实操建议:
- 在
app/Services/(或application/libraries/,取决于 CI 版本)下新建 PHP 类,如OrderService.php,不继承任何 CI 基类,纯 POPO(Plain Old PHP Object) - 构造函数接收明确依赖:比如
OrderRepository、InventoryGateway、LoggerInterface,避免在内部用$this->load->library() - 方法命名体现意图而非技术:用
placeOrder($orderData)而非saveOrderToDb() - 手动实例化或通过简单工厂加载:
new OrderService($orderRepo, $inventory),CI4 可配合Services::inject()管理生命周期
Model 不该是数据库操作封装,而是领域对象或 Repository
CI2/3 的 Model 类常被当成“SQL 拼接器”,CI4 的 Model 虽支持实体映射,但仍默认绑定单表、缺乏组合查询抽象能力。复杂逻辑中,你经常要跨三张表查+聚合+校验+生成凭证,这时 Order_model->get_by_user_and_status() 就显得力不从心。
实操建议:
- 弃用传统
Model做业务查询,改用Repository模式:比如OrderRepository提供findWithItemsAndCoupons($orderId),内部可自由组合 Query Builder、原生 SQL 或调用多个Model - 领域对象(Entity)与数据库模型分离:定义
Order类只含业务属性和方法(如isPayable()),不继承CI_Model,也不带 save/delete 方法 - Repository 返回 Entity 实例,而非数组或
stdClass,保障类型语义清晰
事务边界必须由 Service 显式控制,不能靠 Model 自动提交
CI 的 $this->db->trans_start() 放在 Model 里,容易导致事务粒度失控——比如一个 update_status() 方法自己开事务,而它又被另一个已开启事务的 Service 调用,结果嵌套事务出错或静默失败。
实操建议:
- 事务起始和结束统一放在 Service 方法最外层,例如
placeOrder()开头调$this->db->transStart(),成功后transComplete(),失败则transRollback() - 所有被该 Service 调用的 Repository 或 Gateway 方法,必须假设自己运行在事务上下文中,不做任何
transStart或commit - 若涉及外部 API 调用(如支付网关),需实现补偿逻辑(Compensating Transaction),不能依赖 DB 回滚——比如先落库标记“待支付”,再调支付接口,失败则更新状态为“支付取消”
CI4 的 Factories 和 Services 类能简化依赖注入,但别滥用自动加载
CI4 提供 Config\Services 和 Factories,看起来能自动管理对象创建,但若盲目配置成“每次调用都 new 一次”,反而掩盖了真实依赖关系;若配成单例,又可能引发状态污染(比如共享了未重置的查询条件)。
实操建议:
- 只对无状态类(如
Cache、Email)使用Services::cache()这类快捷访问 - 对含业务状态的 Service(如
CheckoutService),坚持手动构造并传参,确保每次请求都有干净实例 - 避免在
Config\Services中写复杂初始化逻辑,例如 “自动加载当前用户权限并注入到所有 Service”——这会让单元测试无法隔离,也违反单一职责
最易被忽略的一点:复杂业务逻辑必然伴随状态流转(如订单从 created → paid → shipped → completed),CI 没有内置状态机支持,硬用 if-else 判断状态容易漏分支、难审计。真要落地,得引入轻量状态机库(如 symfony/workflow)或至少把状态迁移规则抽成独立策略类,而不是散落在十几个 if ($order->status === 'paid') 里。











