首页 理论 架构 工程 文档 白皮书 著作 研究 案例 下载 博客 关于 开始使用 →

第239章 Controller→Service

第239章 Controller→Service

239.1 提出背景

第236章建立了 Controller 工程,第237章建立了 Model 工程,第238章建立了 View 工程。

在 MVC 工程中,Controller 并不是整个业务系统的处理中心。Controller 的主要作用是接收外部请求、处理进入系统的参数、调用 Service,并将 Service 返回的结果转换为页面或其他响应。

因此:

Controller→Service\boxed{ Controller\rightarrow Service }

是 MVC 中最重要的业务入口关系之一。

完整请求过程为:

Request→Controller→Parameter→Service→Result→Controller→ResponseRequest \rightarrow Controller \rightarrow Parameter \rightarrow Service \rightarrow Result \rightarrow Controller \rightarrow Response

当执行过程中发生异常时,则进入:

Exception→Controller→ErrorResponseException \rightarrow Controller \rightarrow ErrorResponse

因此,本章重点建立五个概念:

Request+Parameter+Service+Return+ExceptionRequest+ Parameter+ Service+ Return+ Exception


239.2 Controller→Service定义

Controller→Service是指 Controller 接收外部请求并完成必要的输入参数处理后,将符合接口要求的参数传递给 Service,由 Service 执行具体业务流程并返回处理结果的工程关系。

定义:

Controller→ServiceController\rightarrow Service

其基本职责为:

Controller=RequestInput+ParameterPreparation+ServiceInvocation+ResponsePreparationController= RequestInput+ ParameterPreparation+ ServiceInvocation+ ResponsePreparation

而:

Service=BusinessProcessService= BusinessProcess

因此:

Controller≠ServiceController\neq Service

Controller 负责:

请求如何进入系统。

Service 负责:

请求进入系统以后应该执行什么业务过程。


239.3 请求

**Request(请求)**是客户端向 ICAI 系统发送的操作要求。

请求可以来自:

  • 浏览器;
  • HTML 表单;
  • AJAX;
  • API;
  • 内部系统;
  • CLI;
  • 其他程序。

基本结构:

Request=Method+URL+Parameters+Headers+Body+SessionRequest= Method+ URL+ Parameters+ Headers+ Body+ Session

例如浏览器请求:

GET /admin/individual/detail.php?id=15

其中:

Method=GETMethod=GET URL=/admin/individual/detail.phpURL=/admin/individual/detail.php Parameter={id=15}Parameter=\{id=15\}

Controller 首先接收这个 Request。


239.4 Request与Controller

请求进入系统后:

Request→ControllerRequest\rightarrow Controller

例如:

class IndividualController
{
    public function detail()
    {
        $id = $_GET['id'];

        // 后续处理
    }
}

这里 Controller 是请求进入 Model 的第一层业务入口。

完整关系:

HTTPRequest→ControllerHTTPRequest \rightarrow Controller

但 Controller 不应该直接完成完整业务计算。

错误:

Request
↓
Controller
↓
查询数据库
↓
计算能力
↓
判断风险
↓
执行决策
↓
修改数据

正确:

Request
↓
Controller
↓
Parameter
↓
Service
↓
Domain / Engine / Repository

239.5 参数

**Parameter(参数)**是 Request 中携带并供业务流程使用的数据。

例如:

id
type
name
status
page
keyword

请求:

GET /admin/individual/detail.php?id=15

得到:

Parameter={id=15}Parameter=\{id=15\}

表单:

POST /admin/individual/store.php

name=Robot
type=machine

得到:

Parameter={name=Robot,type=machine}Parameter= \{ name=Robot, type=machine \}


239.6 参数处理

Controller 对参数进行的是输入层处理,而不是完整业务判断。

基本过程:

Request→ReadParameter→Normalize→Validate→ServiceRequest \rightarrow ReadParameter \rightarrow Normalize \rightarrow Validate \rightarrow Service

例如:

$id = isset($_GET['id'])
    ? (int) $_GET['id']
    : 0;

然后:

if ($id <= 0) {
    return $this->error(
        '参数错误'
    );
}

这里 Controller 解决的是:

请求有没有提供一个基本合法的 id。

而不是解决:

这个 id 对应的机器个体是否允许执行某项业务。

后者属于 Service/Model。


239.7 参数标准化

不同请求可能产生不同格式的数据,因此 Controller 可以进行基础标准化。

例如:

$page = isset($_GET['page'])
    ? (int) $_GET['page']
    : 1;

if ($page < 1) {
    $page = 1;
}

形成:

RawParameter→NormalizedParameterRawParameter \rightarrow NormalizedParameter

例如:

"15"
↓
15

或者:

" robot "
↓
"robot"

这种处理属于输入层。


239.8 参数验证

参数验证可以分为两个层次。

第一层:输入参数验证

由 Controller 处理:

InputValidationInputValidation

例如:

ID>0ID>0 Page≥1Page\geq1 Name≠∅Name\neq\varnothing

第二层:业务规则验证

由 Service/Engine 处理:

BusinessValidationBusinessValidation

例如:

CapabilityAvailable=TrueCapabilityAvailable=True GoalValid=TrueGoalValid=True DecisionAllowed=TrueDecisionAllowed=True

因此:

InputValidation≠BusinessValidationInputValidation\neq BusinessValidation

Controller 不应该把业务规则全部塞入参数验证代码。


239.9 参数对象

当参数较多时,可以使用 Parameter Object。

例如:

class IndividualRequest
{
    protected $id;
    protected $name;
    protected $type;
    protected $status;

    public function getId()
    {
        return $this->id;
    }

    public function getName()
    {
        return $this->name;
    }

    public function getType()
    {
        return $this->type;
    }

    public function getStatus()
    {
        return $this->status;
    }
}

Controller:

$request = new IndividualRequest();

$request->setId($id);
$request->setName($name);
$request->setType($type);

然后:

Controller→RequestObject→ServiceController \rightarrow RequestObject \rightarrow Service

这样可以避免 Service 接收大量无意义的独立参数。


239.10 Service

Service 是 Controller 调用的业务流程对象。

定义:

Service=BusinessProcessService= BusinessProcess

例如:

class IndividualService
{
    public function create($params)
    {
        // 创建机器个体业务流程
    }

    public function find($id)
    {
        // 查询机器个体
    }

    public function update($id, $params)
    {
        // 更新机器个体
    }
}

Controller:

$result = $this->individualService
    ->create($params);

形成:

Controller→IndividualServiceController \rightarrow IndividualService


239.11 Controller调用Service

Controller 调用 Service 的基本形式:

public function store()
{
    $params = $this->getRequestParams();

    $result = $this->individualService
        ->create($params);

    return $this->response(
        $result
    );
}

完整过程:

Request→ControllerRequest \rightarrow Controller Controller→ParameterController \rightarrow Parameter Parameter→ServiceParameter \rightarrow Service Service→ResultService \rightarrow Result Result→ControllerResult \rightarrow Controller


239.12 Service参数边界

Controller 传给 Service 的参数应该是已经完成基础输入处理的数据。

例如:

$params = array(
    'name' => trim($_POST['name']),
    'type' => trim($_POST['type'])
);

然后:

$result = $service->create(
    $params
);

Service 接收到:

NormalizedInputNormalizedInput

而不是完全未经处理的:

RawHTTPInputRawHTTPInput

因此:

HTTP→Controller→NormalizedParameter→ServiceHTTP \rightarrow Controller \rightarrow NormalizedParameter \rightarrow Service


239.13 Service内部执行

Service 接收到参数以后,可以组织完整业务流程。

例如创建机器个体:

CreateIndividualServiceCreateIndividualService

内部:

Parameter→Validate→DomainObject→Engine→Repository→ResultParameter \rightarrow Validate \rightarrow DomainObject \rightarrow Engine \rightarrow Repository \rightarrow Result

例如:

Controller
↓
IndividualService
↓
MachineIndividual
↓
InitializationEngine
↓
VerificationEngine
↓
IndividualRepository
↓
MySQL

Controller 不需要知道这些内部步骤。

因此:

Controller→ServiceController \rightarrow Service

而不是:

Controller→Engine1→Engine2→RepositoryController \rightarrow Engine_1 \rightarrow Engine_2 \rightarrow Repository


239.14 Controller与Engine的边界

Controller 可以间接使用 Engine,但通常不应该直接组织复杂 Engine 调用链。

不推荐:

Controller
↓
GoalEngine
↓
CapabilityEngine
↓
MethodEngine
↓
DecisionEngine
↓
Repository

推荐:

Controller
↓
DecisionService
↓
GoalEngine
↓
CapabilityEngine
↓
MethodEngine
↓
DecisionEngine
↓
Repository

这样:

Controller=入口Controller=入口 Service=业务协调Service=业务协调 Engine=领域计算Engine=领域计算


239.15 返回结果

Service 执行完成以后,需要向 Controller 返回结果。

定义:

ServiceResult=Status+Data+Message+Error+MetaServiceResult= Status+ Data+ Message+ Error+ Meta

例如:

return array(
    'status'  => true,
    'data'    => $individual,
    'message' => '创建成功',
    'error'   => null
);

失败:

return array(
    'status'  => false,
    'data'    => null,
    'message' => '创建失败',
    'error'   => 'individual_invalid'
);

因此:

Service→ServiceResult→ControllerService \rightarrow ServiceResult \rightarrow Controller


239.16 返回结果状态

结果至少需要区分:

ResultStatusResultStatus

例如:

ResultStatus∈{Success,Failed,Blocked,Invalid,NotFound,Pending}ResultStatus\in \{ Success, Failed, Blocked, Invalid, NotFound, Pending \}

这些状态的含义不同。

Success

业务正常完成:

Success=TrueSuccess=True

Failed

业务执行失败:

ExecutionFailed=TrueExecutionFailed=True

Blocked

因为条件、规则、安全或能力限制而无法执行:

Blocked=TrueBlocked=True

Invalid

输入或业务对象不符合要求:

Valid=FalseValid=False

NotFound

目标对象不存在:

Object=∅Object=\varnothing

Pending

业务已经建立,但尚未完成:

Status=PendingStatus=Pending

不能简单使用:

true / false

表达所有业务状态。


239.17 返回数据

Service 可以返回 Domain Object:

Service→DomainObjectService\rightarrow DomainObject

也可以返回计算结果:

Service→ResultService\rightarrow Result

例如:

$result = array(
    'status' => 'success',
    'data' => array(
        'id' => $individual->getId(),
        'type' => $individual->getType()
    )
);

Controller 再将结果转换为:

ViewDataViewData

或者:

JSONResponseJSONResponse

因此:

ServiceResult→Controller→OutputServiceResult \rightarrow Controller \rightarrow Output


239.18 Service异常

业务执行过程中可能出现异常。

例如:

  • Domain Object 创建失败;
  • Repository 查询失败;
  • 数据库连接异常;
  • Engine 计算异常;
  • 参数导致业务无法继续;
  • 文件操作异常;
  • 系统配置异常。

因此:

Service→ExceptionService \rightarrow Exception

异常不是普通业务结果。

Exception≠BusinessResultException\neq BusinessResult

例如:

try {
    $result = $this->service->create(
        $params
    );
} catch (Exception $e) {
    return $this->error(
        '系统处理异常'
    );
}

239.19 业务失败与异常

必须区分:

BusinessFailureBusinessFailure

和:

ExceptionException

例如:

用户创建一个机器个体,但名称为空:

Name=∅Name=\varnothing

这是正常业务验证失败:

BusinessFailureBusinessFailure

而数据库连接突然断开:

DatabaseConnectionErrorDatabaseConnectionError

属于:

ExceptionException

因此:

BusinessFailure≠ExceptionBusinessFailure\neq Exception

业务失败可以作为正常 Service Result 返回:

return array(
    'status' => false,
    'code' => 'INVALID_NAME'
);

而系统异常则通过异常机制处理。


239.20 异常分类

ICAI Controller→Service 可以把异常分为:

Exception=InputException+BusinessException+DomainException+PersistenceException+SystemExceptionException= InputException+ BusinessException+ DomainException+ PersistenceException+ SystemException

InputException

输入参数无法满足接口要求。

BusinessException

业务过程无法继续。

DomainException

领域对象或领域规则出现异常。

PersistenceException

数据库或持久化过程发生异常。

SystemException

系统运行环境发生异常。

这种分类有利于 Controller 做不同响应。


239.21 Service异常处理原则

Service 不应该简单地把所有异常吞掉。

错误:

try {
    // ...
} catch (Exception $e) {
    return null;
}

这种处理会导致:

Exception→nullException\rightarrow null

Controller 无法知道发生了什么。

更合理:

try {
    // business process
} catch (Exception $e) {
    // record log
    throw $e;
}

然后由更上层统一处理。

因此:

Service→Exception→ExceptionHandlerService \rightarrow Exception \rightarrow ExceptionHandler


239.22 Controller异常处理

Controller 是外部请求边界,因此可以负责将异常转换为适合客户端的响应。

例如:

try {

    $result = $this->service->create(
        $params
    );

    return $this->response($result);

} catch (Exception $e) {

    return $this->error(
        '系统处理异常'
    );
}

对于 HTML:

Exception→ErrorPageException \rightarrow ErrorPage

对于 JSON:

Exception→JSONErrorException \rightarrow JSONError

因此:

Exception→ResponseException \rightarrow Response


239.23 不向用户暴露内部异常

数据库异常可能包含:

SQL
Table
FilePath
StackTrace
ServerInformation

这些内容不应该直接输出给普通页面用户。

例如:

错误:

PDOException:
SQLSTATE[42S02]:
Table 'xxx.xxx' doesn't exist
/home/site/app/...

页面应该输出:

系统暂时无法完成操作,请稍后重试。

同时内部日志保留真实异常信息:

Exception→LogException \rightarrow Log

而用户获得:

SafeMessageSafeMessage


239.24 Controller→Service完整流程

完整请求:

Request
↓
Controller
↓
读取参数
↓
参数标准化
↓
输入验证
↓
Service
↓
业务流程
↓
Engine
↓
Domain Object
↓
Repository
↓
Result
↓
Service Return
↓
Controller
↓
ViewData / JSON
↓
Response

异常:

Request
↓
Controller
↓
Service
↓
Exception
↓
Exception Handler
↓
Error Response

239.25 Controller→Service参数方向

正常情况下,数据方向是单向进入:

Request→Controller→ServiceRequest \rightarrow Controller \rightarrow Service

Service 返回:

Service→ControllerService \rightarrow Controller

因此:

Controller↔ServiceController \leftrightarrow Service

但这种双向关系不是任意对象互相调用,而是:

RequestDirection:Controller→ServiceRequestDirection: Controller\rightarrow Service ReturnDirection:Service→ControllerReturnDirection: Service\rightarrow Controller

形成一次完整的请求—返回周期。


239.26 Controller不保存业务状态

Controller 通常是请求级对象。

因此不应该把长期 ICAI 状态直接放进 Controller:

错误:

class IndividualController
{
    protected $individualState;
    protected $knowledge;
    protected $memory;
    protected $capability;
}

真正的 ICAI 状态应该属于 Domain Object:

Individual→StateIndividual \rightarrow State

知识:

Individual→KnowledgeIndividual \rightarrow Knowledge

记忆:

Individual→MemoryIndividual \rightarrow Memory

能力:

Individual→CapabilityIndividual \rightarrow Capability

Controller 只负责本次请求。


239.27 Controller与Service依赖

Controller 可以通过依赖注入获得 Service。

例如:

class IndividualController
{
    protected $service;

    public function __construct(
        IndividualService $service
    ) {
        $this->service = $service;
    }
}

调用:

$result = $this->service->find($id);

形成:

Controller→DependencyServiceController \xrightarrow{Dependency} Service

这种依赖属于第158章对象关系模型中的:

DependencyDependency

即 Controller 为完成请求处理,需要使用 Service。


239.28 Service接口

为了降低 Controller 与具体 Service 实现之间的耦合,可以定义接口:

interface IndividualServiceInterface
{
    public function find($id);

    public function create($params);

    public function update($id, $params);
}

然后:

class IndividualService
    implements IndividualServiceInterface
{
}

Controller 依赖:

IndividualServiceInterfaceIndividualServiceInterface

而不是必须依赖某个具体实现。


239.29 Controller→Service在ICAI中的意义

ICAI 系统存在大量领域流程:

CognitionCognition LearningLearning MatchingMatching DecisionDecision BehaviorBehavior MemoryMemory MaintenanceMaintenance

这些流程都不应该直接写入 Controller。

例如认知:

CognitiveController
↓
CognitiveService
↓
CognitiveEngine

决策:

DecisionController
↓
DecisionService
↓
DecisionEngine

行为:

BehaviorController
↓
BehaviorService
↓
BehaviorEngine

维护:

MaintenanceController
↓
MaintenanceService
↓
MaintenanceEngine

因此:

Controller→Service→EngineController \rightarrow Service \rightarrow Engine

成为 ICAI 各领域统一的工程入口结构。


239.30 Controller→Service与ICAI运行

ICAI 的运行过程:

Cognition→Need→Goal→Capability→Matching→Method→Decision→Behavior→Action→ResultCognition \rightarrow Need \rightarrow Goal \rightarrow Capability \rightarrow Matching \rightarrow Method \rightarrow Decision \rightarrow Behavior \rightarrow Action \rightarrow Result

不能全部放在 Controller。

应该按照领域 Service 进行组织。

例如:

CognitiveService
        ↓
NeedService
        ↓
GoalService
        ↓
CapabilityService
        ↓
MatchingService
        ↓
MethodService
        ↓
DecisionService
        ↓
BehaviorService
        ↓
ResultService

每一个 Service 再调用对应 Engine 和 Domain Object。

因此:

ControllerController

只是整个 ICAI Runtime 的入口之一。


239.31 Controller→Service异常闭环

正常:

Request→Controller→Service→Result→ControllerRequest \rightarrow Controller \rightarrow Service \rightarrow Result \rightarrow Controller

异常:

Request→Controller→Service→ExceptionRequest \rightarrow Controller \rightarrow Service \rightarrow Exception

然后:

Exception→LogException \rightarrow Log

同时:

Exception→SafeResponseException \rightarrow SafeResponse

因此:

Exception→Record→Handle→Respond\boxed{ Exception \rightarrow Record \rightarrow Handle \rightarrow Respond }

异常处理本身也应该成为系统工程的一部分。


239.32 Controller→Service工程代码结构

一个完整的 Controller 可以保持简单:

class IndividualController
{
    protected $service;
    protected $view;

    public function store()
    {
        try {

            $params = $this->getParams();

            $result =
                $this->service->create(
                    $params
                );

            return $this->view->display(
                'individual/result.tpl',
                $result
            );

        } catch (Exception $e) {

            return $this->handleException(
                $e
            );
        }
    }
}

真正的业务过程进入:

class IndividualService
{
    public function create($params)
    {
        // 参数业务验证
        // 创建Domain Object
        // Engine处理
        // Repository保存
        // 返回结果
    }
}

形成:

Controller=ThinController=Thin Service=BusinessProcessService=BusinessProcess

这就是 Controller→Service 的核心工程思想。


239.33 Controller→Service完整模型

最终可以建立:

Request→Controller→Parameter→Service→BusinessProcess→Result→Controller→Response\boxed{ Request \rightarrow Controller \rightarrow Parameter \rightarrow Service \rightarrow BusinessProcess \rightarrow Result \rightarrow Controller \rightarrow Response }

异常分支:

Service→Exception→Handler→SafeResponse\boxed{ Service \rightarrow Exception \rightarrow Handler \rightarrow SafeResponse }

其中:

Request=外部请求Request=外部请求 Parameter=请求参数Parameter=请求参数 Service=业务流程Service=业务流程 Result=业务结果Result=业务结果 Exception=异常状态Exception=异常状态


239.34 本章总结

Controller→Service 是 ICAI MVC 中连接外部请求与内部业务模型的核心工程关系。

其基本流程是:

Request→Controller→Parameter→Service\boxed{ Request \rightarrow Controller \rightarrow Parameter \rightarrow Service }

Service 执行业务过程:

Service→DomainObject→Engine→Repository\boxed{ Service \rightarrow DomainObject \rightarrow Engine \rightarrow Repository }

然后返回:

Result→Controller→View/JSON→Response\boxed{ Result \rightarrow Controller \rightarrow View/JSON \rightarrow Response }

异常则形成:

Service→Exception→Handler→SafeResponse\boxed{ Service \rightarrow Exception \rightarrow Handler \rightarrow SafeResponse }

因此五个核心概念可以统一为:

Request→Parameter→Service→Return→Exception\boxed{ Request \rightarrow Parameter \rightarrow Service \rightarrow Return \rightarrow Exception }

其中 Controller 的核心职责是:

接收请求、处理输入、调用Service、接收结果、返回响应\boxed{ 接收请求、处理输入、调用Service、接收结果、返回响应 }

而 Service 的核心职责是:

组织业务流程、调用领域对象、调用Engine、协调Repository并产生业务结果\boxed{ 组织业务流程、调用领域对象、调用Engine、协调Repository并产生业务结果 }

最终形成 ICAI MVC 的稳定入口:

Controller→Service→Model→Result\boxed{ Controller \rightarrow Service \rightarrow Model \rightarrow Result }

并通过 View 完成:

Result→ViewData→Smarty→HTML\boxed{ Result \rightarrow ViewData \rightarrow Smarty \rightarrow HTML }

这样 Controller 保持简单,Service 承担业务流程,Engine 承担领域计算,Domain Object 承担领域对象,Repository 承担持久化,各层职责清晰且可以独立维护。

239.35 可运行完整代码示例

为了使 Controller→Service 的工程关系能够直接运行,本节建立一个最小 ICAI MVC 示例。

示例实现:

Request→Controller→Parameter→Service→DomainObject→Repository→Result→Controller→ResponseRequest \rightarrow Controller \rightarrow Parameter \rightarrow Service \rightarrow DomainObject \rightarrow Repository \rightarrow Result \rightarrow Controller \rightarrow Response

异常:

Service→Exception→Controller→ErrorResponseService \rightarrow Exception \rightarrow Controller \rightarrow ErrorResponse

本示例使用 PHP 原生代码,可以直接在 PHP CLI 环境中运行。


239.36 完整运行代码

建立文件:

controller_service_demo.php

完整代码如下:

<?php

/**
 * ICAI Controller -> Service
 *
 * PHP CLI 可直接运行:
 *
 * php controller_service_demo.php
 *
 * 本示例不依赖数据库。
 * Repository 使用内存数组模拟数据存储。
 */

/**
 * =========================================================
 * 1. Domain Object
 * =========================================================
 *
 * MachineIndividual
 * 机器个体领域对象
 */
class MachineIndividual
{
    protected $id;
    protected $name;
    protected $type;
    protected $status;

    public function __construct(
        $id,
        $name,
        $type,
        $status = 'created'
    ) {
        $this->id = $id;
        $this->name = $name;
        $this->type = $type;
        $this->status = $status;
    }

    public function getId()
    {
        return $this->id;
    }

    public function getName()
    {
        return $this->name;
    }

    public function getType()
    {
        return $this->type;
    }

    public function getStatus()
    {
        return $this->status;
    }

    public function setName($name)
    {
        $this->name = $name;
    }

    public function setType($type)
    {
        $this->type = $type;
    }

    public function setStatus($status)
    {
        $this->status = $status;
    }

    public function toArray()
    {
        return array(
            'id'     => $this->id,
            'name'   => $this->name,
            'type'   => $this->type,
            'status' => $this->status
        );
    }
}


/**
 * =========================================================
 * 2. Repository
 * =========================================================
 *
 * 这里使用数组模拟 MySQL。
 *
 * 真实项目中可以替换为:
 *
 * IndividualRepository
 *       ↓
 * PDO
 *       ↓
 * MySQL
 */
class IndividualRepository
{
    protected $data = array();

    protected $nextId = 1;

    public function create(MachineIndividual $individual)
    {
        $id = $this->nextId++;

        $individual = new MachineIndividual(
            $id,
            $individual->getName(),
            $individual->getType(),
            $individual->getStatus()
        );

        $this->data[$id] = $individual;

        return $individual;
    }

    public function find($id)
    {
        $id = (int) $id;

        if (isset($this->data[$id])) {
            return $this->data[$id];
        }

        return null;
    }

    public function update(MachineIndividual $individual)
    {
        $id = $individual->getId();

        if (!isset($this->data[$id])) {
            return false;
        }

        $this->data[$id] = $individual;

        return true;
    }

    public function all()
    {
        return array_values($this->data);
    }
}


/**
 * =========================================================
 * 3. Service Exception
 * =========================================================
 *
 * Service 层业务异常。
 */
class BusinessException extends Exception
{
}


/**
 * =========================================================
 * 4. Service
 * =========================================================
 *
 * Service 负责业务流程。
 *
 * Controller 不直接操作 Repository。
 */
class IndividualService
{
    protected $repository;

    public function __construct(
        IndividualRepository $repository
    ) {
        $this->repository = $repository;
    }

    /**
     * 创建机器个体
     */
    public function create($params)
    {
        /**
         * -----------------------------------------
         * 4.1 业务参数验证
         * -----------------------------------------
         */

        $name = isset($params['name'])
            ? trim($params['name'])
            : '';

        $type = isset($params['type'])
            ? trim($params['type'])
            : '';

        if ($name === '') {
            throw new BusinessException(
                '机器个体名称不能为空'
            );
        }

        if ($type === '') {
            throw new BusinessException(
                '机器个体类型不能为空'
            );
        }

        /**
         * -----------------------------------------
         * 4.2 创建 Domain Object
         * -----------------------------------------
         */

        $individual = new MachineIndividual(
            0,
            $name,
            $type,
            'created'
        );

        /**
         * -----------------------------------------
         * 4.3 Repository 持久化
         * -----------------------------------------
         */

        $individual =
            $this->repository->create(
                $individual
            );

        /**
         * -----------------------------------------
         * 4.4 返回业务结果
         * -----------------------------------------
         */

        return array(
            'status'  => 'success',
            'message' => '机器个体创建成功',
            'data'    => $individual->toArray()
        );
    }


    /**
     * 查询机器个体
     */
    public function find($id)
    {
        $id = (int) $id;

        if ($id <= 0) {
            throw new BusinessException(
                '机器个体ID无效'
            );
        }

        $individual =
            $this->repository->find($id);

        if ($individual === null) {
            return array(
                'status'  => 'not_found',
                'message' => '机器个体不存在',
                'data'    => null
            );
        }

        return array(
            'status'  => 'success',
            'message' => '查询成功',
            'data'    => $individual->toArray()
        );
    }


    /**
     * 修改机器个体
     */
    public function update($id, $params)
    {
        $id = (int) $id;

        if ($id <= 0) {
            throw new BusinessException(
                '机器个体ID无效'
            );
        }

        $individual =
            $this->repository->find($id);

        if ($individual === null) {
            return array(
                'status'  => 'not_found',
                'message' => '机器个体不存在',
                'data'    => null
            );
        }

        if (isset($params['name'])) {
            $name = trim($params['name']);

            if ($name === '') {
                throw new BusinessException(
                    '名称不能为空'
                );
            }

            $individual->setName($name);
        }

        if (isset($params['type'])) {
            $type = trim($params['type']);

            if ($type === '') {
                throw new BusinessException(
                    '类型不能为空'
                );
            }

            $individual->setType($type);
        }

        $this->repository->update(
            $individual
        );

        return array(
            'status'  => 'success',
            'message' => '机器个体更新成功',
            'data'    => $individual->toArray()
        );
    }
}


/**
 * =========================================================
 * 5. Controller
 * =========================================================
 *
 * Controller 负责:
 *
 * Request
 * Parameter
 * Service
 * Return
 * Exception
 *
 * Controller 不直接访问 Repository。
 */
class IndividualController
{
    protected $service;

    public function __construct(
        IndividualService $service
    ) {
        $this->service = $service;
    }


    /**
     * 处理创建请求
     */
    public function store($request)
    {
        try {

            /**
             * -------------------------------------
             * 5.1 Request
             * -------------------------------------
             */

            $params =
                $this->getStoreParams(
                    $request
                );

            /**
             * -------------------------------------
             * 5.2 Service
             * -------------------------------------
             */

            $result =
                $this->service->create(
                    $params
                );

            /**
             * -------------------------------------
             * 5.3 Return
             * -------------------------------------
             */

            return $this->success(
                $result
            );

        } catch (BusinessException $e) {

            /**
             * -------------------------------------
             * 5.4 Business Exception
             * -------------------------------------
             */

            return $this->businessError(
                $e
            );

        } catch (Exception $e) {

            /**
             * -------------------------------------
             * 5.5 System Exception
             * -------------------------------------
             */

            return $this->systemError(
                $e
            );
        }
    }


    /**
     * 查询请求
     */
    public function detail($request)
    {
        try {

            /**
             * -------------------------------------
             * Request
             * -------------------------------------
             */

            $id = isset($request['id'])
                ? (int) $request['id']
                : 0;

            /**
             * -------------------------------------
             * Parameter Validation
             * -------------------------------------
             */

            if ($id <= 0) {
                return $this->error(
                    '参数错误:id必须大于0'
                );
            }

            /**
             * -------------------------------------
             * Service
             * -------------------------------------
             */

            $result =
                $this->service->find($id);

            /**
             * -------------------------------------
             * Return
             * -------------------------------------
             */

            return $this->response(
                $result
            );

        } catch (BusinessException $e) {

            return $this->businessError(
                $e
            );

        } catch (Exception $e) {

            return $this->systemError(
                $e
            );
        }
    }


    /**
     * 修改请求
     */
    public function update($request)
    {
        try {

            /**
             * -------------------------------------
             * Request
             * -------------------------------------
             */

            $id = isset($request['id'])
                ? (int) $request['id']
                : 0;

            /**
             * -------------------------------------
             * Parameter
             * -------------------------------------
             */

            $params = array();

            if (isset($request['name'])) {
                $params['name'] =
                    trim($request['name']);
            }

            if (isset($request['type'])) {
                $params['type'] =
                    trim($request['type']);
            }

            /**
             * -------------------------------------
             * Service
             * -------------------------------------
             */

            $result =
                $this->service->update(
                    $id,
                    $params
                );

            /**
             * -------------------------------------
             * Return
             * -------------------------------------
             */

            return $this->response(
                $result
            );

        } catch (BusinessException $e) {

            return $this->businessError(
                $e
            );

        } catch (Exception $e) {

            return $this->systemError(
                $e
            );
        }
    }


    /**
     * Controller层参数处理
     */
    protected function getStoreParams(
        $request
    ) {
        $name = isset($request['name'])
            ? trim($request['name'])
            : '';

        $type = isset($request['type'])
            ? trim($request['type'])
            : '';

        /**
         * 输入层验证
         */
        if ($name === '') {
            throw new BusinessException(
                '参数错误:name不能为空'
            );
        }

        if ($type === '') {
            throw new BusinessException(
                '参数错误:type不能为空'
            );
        }

        return array(
            'name' => $name,
            'type' => $type
        );
    }


    /**
     * 成功响应
     */
    protected function success($result)
    {
        return $this->response(
            $result
        );
    }


    /**
     * 普通错误
     */
    protected function error($message)
    {
        return array(
            'status'  => 'error',
            'message' => $message,
            'data'    => null
        );
    }


    /**
     * 业务异常响应
     */
    protected function businessError(
        BusinessException $e
    ) {
        return array(
            'status'  => 'business_error',
            'message' => $e->getMessage(),
            'data'    => null
        );
    }


    /**
     * 系统异常响应
     */
    protected function systemError(
        Exception $e
    ) {
        /**
         * 真实项目中这里应该写入日志。
         */
        error_log(
            $e->getMessage()
        );

        /**
         * 不直接把内部异常信息返回给用户。
         */
        return array(
            'status'  => 'system_error',
            'message' => '系统处理异常',
            'data'    => null
        );
    }


    /**
     * 统一响应
     */
    protected function response($result)
    {
        return $result;
    }
}


/**
 * =========================================================
 * 6. Application Bootstrap
 * =========================================================
 *
 * 创建对象依赖关系:
 *
 * Repository
 *     ↓
 * Service
 *     ↓
 * Controller
 */
$repository =
    new IndividualRepository();

$service =
    new IndividualService(
        $repository
    );

$controller =
    new IndividualController(
        $service
    );


/**
 * =========================================================
 * 7. 模拟 Request
 * =========================================================
 */

echo "=============================\n";
echo "1. 创建机器个体\n";
echo "=============================\n";

$request = array(
    'name' => 'Robot-001',
    'type' => 'robot'
);

$result =
    $controller->store(
        $request
    );

print_r($result);


/**
 * =========================================================
 * 8. 查询
 * =========================================================
 */

echo "\n";
echo "=============================\n";
echo "2. 查询机器个体\n";
echo "=============================\n";

$request = array(
    'id' => 1
);

$result =
    $controller->detail(
        $request
    );

print_r($result);


/**
 * =========================================================
 * 9. 修改
 * =========================================================
 */

echo "\n";
echo "=============================\n";
echo "3. 修改机器个体\n";
echo "=============================\n";

$request = array(
    'id'   => 1,
    'name' => 'Robot-001-Updated',
    'type' => 'industrial_robot'
);

$result =
    $controller->update(
        $request
    );

print_r($result);


/**
 * =========================================================
 * 10. 再次查询
 * =========================================================
 */

echo "\n";
echo "=============================\n";
echo "4. 查询更新后的机器个体\n";
echo "=============================\n";

$request = array(
    'id' => 1
);

$result =
    $controller->detail(
        $request
    );

print_r($result);


/**
 * =========================================================
 * 11. 测试业务异常
 * =========================================================
 */

echo "\n";
echo "=============================\n";
echo "5. 测试业务异常\n";
echo "=============================\n";

$request = array(
    'name' => '',
    'type' => 'robot'
);

$result =
    $controller->store(
        $request
    );

print_r($result);


/**
 * =========================================================
 * 12. 测试对象不存在
 * =========================================================
 */

echo "\n";
echo "=============================\n";
echo "6. 测试对象不存在\n";
echo "=============================\n";

$request = array(
    'id' => 999
);

$result =
    $controller->detail(
        $request
    );

print_r($result);


/**
 * =========================================================
 * 13. 测试参数异常
 * =========================================================
 */

echo "\n";
echo "=============================\n";
echo "7. 测试参数异常\n";
echo "=============================\n";

$request = array(
    'id' => 0
);

$result =
    $controller->detail(
        $request
    );

print_r($result);


239.37 运行方式

将代码保存为:

controller_service_demo.php

然后在 PHP 环境中执行:

php controller_service_demo.php

不需要 MySQL,也不需要 Composer。

运行结果类似:

=============================
1. 创建机器个体
=============================
Array
(
    [status] => success
    [message] => 机器个体创建成功
    [data] => Array
        (
            [id] => 1
            [name] => Robot-001
            [type] => robot
            [status] => created
        )
)

=============================
2. 查询机器个体
=============================
Array
(
    [status] => success
    [message] => 查询成功
    [data] => Array
        (
            [id] => 1
            [name] => Robot-001
            [type] => robot
            [status] => created
        )
)

更新以后:

=============================
4. 查询更新后的机器个体
=============================
Array
(
    [status] => success
    [message] => 查询成功
    [data] => Array
        (
            [id] => 1
            [name] => Robot-001-Updated
            [type] => industrial_robot
            [status] => created
        )
)

参数错误:

=============================
7. 测试参数异常
=============================
Array
(
    [status] => error
    [message] => 参数错误:id必须大于0
    [data] =>
)

239.38 代码结构对应理论

这段代码不是简单的 PHP 示例,而是直接对应 ICAI 工程模型。

第一层:

Request

对应:

RequestRequest

第二层:

IndividualController

对应:

ControllerController

第三层:

IndividualService

对应:

ServiceService

第四层:

MachineIndividual

对应:

DomainObjectDomainObject

第五层:

IndividualRepository

对应:

RepositoryRepository

最终形成:

Request→Controller→Service→DomainObject→Repository\boxed{ Request \rightarrow Controller \rightarrow Service \rightarrow DomainObject \rightarrow Repository }


239.39 Controller代码边界

Controller 中最重要的代码是:

$params =
    $this->getStoreParams(
        $request
    );

$result =
    $this->service->create(
        $params
    );

return $this->response(
    $result
);

它表达:

Request→Parameter→Service→ReturnRequest \rightarrow Parameter \rightarrow Service \rightarrow Return

Controller 没有:

↓
直接访问 MySQL
↓
直接创建 PDO
↓
直接执行 SQL
↓
直接计算 ICAI 能力
↓
直接执行决策

因此 Controller 保持较薄。


239.40 Service代码边界

Service 中:

public function create($params)
{
    // 参数业务验证

    // 创建 Domain Object

    // Repository 持久化

    // 返回业务结果
}

这里体现:

Service=BusinessProcessService= BusinessProcess

以后随着 ICAI 工程继续扩展,可以变成:

IndividualService
↓
IndividualInitializationEngine
↓
CapabilityEngine
↓
VerificationEngine
↓
IndividualRepository

或者:

DecisionService
↓
DecisionEngine
↓
RiskEngine
↓
MethodSelectionEngine
↓
DecisionRepository

Controller 不需要知道这些内部过程。


239.41 Repository代码边界

本示例中的:

class IndividualRepository

使用:

protected $data = array();

模拟数据库。

真实 MySQL 工程中可以替换为:

class IndividualRepository
{
    protected $pdo;

    public function __construct(PDO $pdo)
    {
        $this->pdo = $pdo;
    }

    public function find($id)
    {
        $sql = "
            SELECT
                id,
                name,
                type,
                status
            FROM individuals
            WHERE id = :id
            LIMIT 1
        ";

        $statement =
            $this->pdo->prepare($sql);

        $statement->execute(
            array(
                ':id' => $id
            )
        );

        $row =
            $statement->fetch(
                PDO::FETCH_ASSOC
            );

        if (!$row) {
            return null;
        }

        return new MachineIndividual(
            $row['id'],
            $row['name'],
            $row['type'],
            $row['status']
        );
    }
}

这样:

Repository→PDO→MySQLRepository \rightarrow PDO \rightarrow MySQL

而 Service 和 Controller 的接口基本不需要改变。


239.42 从内存Repository到MySQL

本章示例:

Service→MemoryRepositoryService \rightarrow MemoryRepository

生产工程:

Service→MySQLRepository→PDO→MySQLService \rightarrow MySQLRepository \rightarrow PDO \rightarrow MySQL

这正是 Repository 的意义:

业务层不直接依赖数据库实现\boxed{ 业务层不直接依赖数据库实现 }

因此可以:

MemoryRepository↔MySQLRepositoryMemoryRepository \leftrightarrow MySQLRepository

而:

IndividualServiceIndividualService

保持基本不变。


239.43 Controller→Service完整对象关系

对象依赖关系为:

Controller→DependsOnServiceController \xrightarrow{DependsOn} Service

Service:

Service→DependsOnRepositoryService \xrightarrow{DependsOn} Repository

Service 创建:

Service→CreatesDomainObjectService \xrightarrow{Creates} DomainObject

Repository 保存:

Repository→PersistsDomainObjectRepository \xrightarrow{Persists} DomainObject

最终:

IndividualController
        │
        │ depends on
        ↓
IndividualService
        │
        ├──── creates ────→ MachineIndividual
        │
        └──── depends on ─→ IndividualRepository
                                  │
                                  ↓
                               Storage

这正好对应第158章中的对象关系模型:

DependencyDependency

以及:

CompositionComposition


239.44 Controller→Service异常关系

异常关系可以表示为:

Request
   ↓
Controller
   ↓
Service
   ↓
Exception
   ↓
Controller
   ↓
ErrorResponse

业务异常:

BusinessExceptionBusinessException

系统异常:

SystemExceptionSystemException

最终:

Exception→ErrorResponseException \rightarrow ErrorResponse

但真实内部异常信息应该进入:

Exception→LogException \rightarrow Log

而不是直接进入用户页面。


239.45 与ICAI核心理论的关系

本示例虽然只实现“创建、查询、更新机器个体”,但它可以继续向 ICAI 核心理论扩展。

例如:

IndividualController
↓
IndividualService
↓
MachineIndividual
↓
CognitiveEngine
↓
GoalEngine
↓
CapabilityEngine
↓
MatchingEngine
↓
MethodEngine
↓
DecisionEngine
↓
BehaviorEngine
↓
FeedbackEngine
↓
MemoryEngine
↓
MaintenanceEngine
↓
Repository
↓
MySQL

最终:

Controller→Service→ICAI Runtime→Repository\boxed{ Controller \rightarrow Service \rightarrow ICAI\ Runtime \rightarrow Repository }

因此 Controller→Service 并不是独立于 ICAI 理论之外的普通 Web 工程,而是 ICAI 理论进入实际系统运行环境的入口。


239.46 本节最终模型

本章 Controller→Service 可以最终形式化为:

Request→Controller→Parameter→Service→BusinessProcess→Result→Controller→Response\boxed{ Request \rightarrow Controller \rightarrow Parameter \rightarrow Service \rightarrow BusinessProcess \rightarrow Result \rightarrow Controller \rightarrow Response }

异常:

Service→Exception→Log→Controller→SafeResponse\boxed{ Service \rightarrow Exception \rightarrow Log \rightarrow Controller \rightarrow SafeResponse }

工程对象:

Controller→DependencyService→DependencyRepository\boxed{ Controller \xrightarrow{Dependency} Service \xrightarrow{Dependency} Repository }

业务对象:

Service→DomainObject→Engine→Repository\boxed{ Service \rightarrow DomainObject \rightarrow Engine \rightarrow Repository }

因此,本章最终建立:

Controller=请求入口\boxed{ Controller=请求入口 } Service=业务流程\boxed{ Service=业务流程 } DomainObject=业务对象\boxed{ DomainObject=业务对象 } Engine=领域计算\boxed{ Engine=领域计算 } Repository=数据持久化\boxed{ Repository=数据持久化 }

形成完整的 ICAI Web 工程链:

HTTP→Controller→Service→DomainObject→Engine→Repository→MySQL\boxed{ HTTP \rightarrow Controller \rightarrow Service \rightarrow DomainObject \rightarrow Engine \rightarrow Repository \rightarrow MySQL }

再返回:

MySQL→Repository→DomainObject→Service→Controller→View→HTML\boxed{ MySQL \rightarrow Repository \rightarrow DomainObject \rightarrow Service \rightarrow Controller \rightarrow View \rightarrow HTML }

这就是 ICAI 从理论模型进入可执行 PHP MVC 系统的一个最小、完整、可运行的 Controller→Service 实现。

Leave a Reply

Your email address will not be published. Required fields are marked *