第242章 Repository→MySQL
242.1 提出背景
第239章建立:
Controller→ServiceController\rightarrow Service
第240章建立:
Service→EngineService\rightarrow Engine
第241章建立:
Engine→DomainObjectEngine\rightarrow DomainObject
经过这三个层次以后,ICAI 系统已经能够接收业务请求、组织业务流程、执行领域计算,并修改 Domain Object。
但是 Domain Object 在内存中的变化不能永久存在。
机器个体需要:
- 查询已有数据;
- 保存新对象;
- 更新已有对象;
- 删除不再需要的数据。
因此需要建立持久化关系:
Repository→MySQL\boxed{ Repository\rightarrow MySQL }
完整工程链进一步形成:
Controller→Service→Engine→DomainObject→Repository→MySQL\boxed{ Controller \rightarrow Service \rightarrow Engine \rightarrow DomainObject \rightarrow Repository \rightarrow MySQL }
其中:
Repository=持久化访问层Repository=持久化访问层 MySQL=关系型数据库MySQL=关系型数据库
Repository 不负责领域计算,MySQL 也不负责 ICAI 业务逻辑。
本章重点建立四种基础数据操作:
QueryQuery SaveSave UpdateUpdate DeleteDelete
242.2 Repository→MySQL定义
Repository→MySQL是指 Repository 根据 Domain Object 和业务持久化要求,通过数据库访问机制向 MySQL 执行数据查询、保存、更新和删除等持久化操作,并将数据库记录转换为系统能够使用的数据或 Domain Object。
基本关系:
Repository→MySQL\boxed{ Repository\rightarrow MySQL }
完整过程:
DomainObject→Repository→MySQLDomainObject \rightarrow Repository \rightarrow MySQL
查询时则反向:
MySQL→Repository→DomainObjectMySQL \rightarrow Repository \rightarrow DomainObject
因此 Repository 与 MySQL 之间形成:
Repository↔MySQL\boxed{ Repository \leftrightarrow MySQL }
但这种双向关系并不意味着 Repository 和数据库职责相同。
Repository 负责:
如何按照系统的领域需求访问数据。
MySQL 负责:
如何存储和管理关系型数据。
242.3 Repository
**Repository(仓储)**是 Domain Object 与持久化数据之间的访问抽象。
它的主要职责是:
Repository=Query+Save+Update+DeleteRepository= Query+ Save+ Update+ Delete
例如:
class IndividualRepository
{
public function find($id)
{
}
public function save($individual)
{
}
public function update($individual)
{
}
public function delete($id)
{
}
}
Service 不需要知道具体 SQL:
$individual =
$repository->find($id);
而不是:
$sql = "SELECT * FROM individuals WHERE id = ?";
因此:
Service→Repository\boxed{ Service\rightarrow Repository }
而:
Repository→MySQL\boxed{ Repository\rightarrow MySQL }
242.4 Repository与MySQL的职责边界
Repository 和 MySQL 必须保持职责分离。
Repository≠MySQLRepository\neq MySQL
Repository 是 PHP 工程对象。
MySQL 是数据库系统。
Repository 负责:
- 组织查询;
- 参数绑定;
- 数据转换;
- Domain Object 映射;
- 保存;
- 更新;
- 删除;
- 持久化异常处理。
MySQL 负责:
- 表;
- 字段;
- 索引;
- 数据存储;
- SQL执行;
- 事务;
- 约束。
因此:
PHP Object→Repository→PDO→MySQLPHP\ Object \rightarrow Repository \rightarrow PDO \rightarrow MySQL
242.5 数据查询
**数据查询(Query)**是从 MySQL 中读取持久化数据并返回系统使用的数据或 Domain Object 的过程。
基本关系:
MySQL→Repository→DomainObject\boxed{ MySQL \rightarrow Repository \rightarrow DomainObject }
例如:
$individual =
$repository->find(15);
Repository:
public function find($id)
{
$sql = "
SELECT
id,
name,
type,
state
FROM individuals
WHERE id = :id
";
$statement =
$this->pdo->prepare($sql);
$statement->execute(
array(':id' => $id)
);
$row =
$statement->fetch(
PDO::FETCH_ASSOC
);
if (!$row) {
return null;
}
return $this->mapToDomain(
$row
);
}
数据流:
ID→Repository→SQL→MySQL→Record→DomainObjectID \rightarrow Repository \rightarrow SQL \rightarrow MySQL \rightarrow Record \rightarrow DomainObject
242.6 查询与Domain Object
Repository 查询的最终目标不是简单返回一行数组,而是根据系统设计将数据库记录转换成 Domain Object。
数据库:
id = 15
name = Robot A
type = machine
state = idle
经过:
DatabaseRecord→RepositoryDatabaseRecord \rightarrow Repository
转换为:
$individual
即:
DatabaseRecord→DomainObjectDatabaseRecord \rightarrow DomainObject
例如:
protected function mapToDomain(
array $row
) {
$individual =
new Individual();
$individual->setId(
(int)$row['id']
);
$individual->setName(
$row['name']
);
$individual->setType(
$row['type']
);
$individual->setState(
$row['state']
);
return $individual;
}
这样:
DomainObject≠DatabaseRecordDomainObject \neq DatabaseRecord
而是:
DatabaseRecord→RepositoryDomainObject\boxed{ DatabaseRecord \xrightarrow{Repository} DomainObject }
242.7 查询条件
Repository 可以提供不同查询方法。
例如:
find($id)
表示按照 ID 查询。
findByType($type)
表示按照类型查询。
findByState($state)
表示按照状态查询。
findAll()
表示查询全部。
因此:
Repository=QueryMethodsRepository= QueryMethods
例如:
public function findByState($state)
{
$sql = "
SELECT *
FROM individuals
WHERE state = :state
";
$statement =
$this->pdo->prepare($sql);
$statement->execute(
array(':state' => $state)
);
return $statement->fetchAll(
PDO::FETCH_ASSOC
);
}
242.8 查询安全
Repository 执行 SQL 时必须使用参数绑定。
不推荐:
$sql =
"SELECT * FROM individuals
WHERE id = " . $id;
推荐:
$sql = "
SELECT *
FROM individuals
WHERE id = :id
";
$statement =
$this->pdo->prepare($sql);
$statement->execute(
array(':id' => $id)
);
形成:
Parameter→PreparedStatement→MySQLParameter \rightarrow PreparedStatement \rightarrow MySQL
这样可以避免将外部字符串直接拼接进入 SQL。
242.9 数据保存
**数据保存(Save)**是将新的 Domain Object 转换为数据库记录,并写入 MySQL 的过程。
基本关系:
DomainObject→Repository→MySQL\boxed{ DomainObject \rightarrow Repository \rightarrow MySQL }
例如:
$individual =
new Individual();
$individual->setName(
'Robot A'
);
$individual->setType(
'machine'
);
$individual->setState(
'idle'
);
$repository->save(
$individual
);
Repository:
public function save(
Individual $individual
) {
$sql = "
INSERT INTO individuals
(
name,
type,
state
)
VALUES
(
:name,
:type,
:state
)
";
$statement =
$this->pdo->prepare($sql);
$statement->execute(
array(
':name' =>
$individual->getName(),
':type' =>
$individual->getType(),
':state' =>
$individual->getState()
)
);
$individual->setId(
$this->pdo->lastInsertId()
);
return $individual;
}
242.10 保存与创建
数据保存通常发生在 Domain Object 创建以后。
过程:
Create→Initialize→DomainObject→SaveCreate \rightarrow Initialize \rightarrow DomainObject \rightarrow Save
例如:
创建机器个体
↓
初始化属性
↓
初始化状态
↓
形成Domain Object
↓
Repository
↓
INSERT
↓
MySQL
因此:
DomainObject→Repository→INSERTDomainObject \rightarrow Repository \rightarrow INSERT
最终形成持久化对象。
242.11 保存后的ID
如果数据库使用自增主键,那么新对象保存以后会获得数据库生成的 ID。
例如:
保存前:
id = null
执行:
INSERT
以后:
保存后:
id = 15
因此:
Object.id=∅Object.id=\varnothing
经过:
Repository.save()Repository.save()
变为:
Object.id=15Object.id=15
这是 Domain Object 与数据库生命周期连接的重要过程。
242.12 数据更新
**数据更新(Update)**是将已经存在的 Domain Object 的最新状态同步到 MySQL 的过程。
基本关系:
DomainObjectnew→Repository→UPDATE→MySQL\boxed{ DomainObject_{new} \rightarrow Repository \rightarrow UPDATE \rightarrow MySQL }
例如:
原状态:
id = 15
state = idle
Engine 计算以后:
state = running
然后:
$individual->setState(
'running'
);
$repository->update(
$individual
);
数据库:
UPDATE individuals
SET state = :state
WHERE id = :id
形成:
idle→runningidle \rightarrow running
最终数据库同步:
DatabaseStatet→DatabaseStatet+1DatabaseState_t \rightarrow DatabaseState_{t+1}
242.13 Update与Engine状态变化
第241章建立:
Engine→DomainObject→StateUpdateEngine \rightarrow DomainObject \rightarrow StateUpdate
本章继续:
DomainObject→Repository→MySQLDomainObject \rightarrow Repository \rightarrow MySQL
因此两个章节连接为:
Engine→DomainObject→Repository→MySQL\boxed{ Engine \rightarrow DomainObject \rightarrow Repository \rightarrow MySQL }
例如:
数据库:
idle
↓ Repository查询
Domain Object:
idle
↓ Engine计算
Domain Object:
running
↓ Repository更新
数据库:
running
这形成完整的状态持久化链。
242.14 更新方法
Repository 可以定义:
public function update(
Individual $individual
) {
$sql = "
UPDATE individuals
SET
name = :name,
type = :type,
state = :state
WHERE id = :id
";
$statement =
$this->pdo->prepare($sql);
return $statement->execute(
array(
':id' =>
$individual->getId(),
':name' =>
$individual->getName(),
':type' =>
$individual->getType(),
':state' =>
$individual->getState()
)
);
}
这里:
WHERE id=:idWHERE\ id=:id
决定更新哪一个 Domain Object。
因此:
ObjectID→DatabaseRecordObjectID \rightarrow DatabaseRecord
是更新过程的重要标识。
242.15 数据删除
**数据删除(Delete)**是从持久化存储中删除指定数据记录的过程。
基本关系:
DomainIdentity→Repository→DELETE→MySQL\boxed{ DomainIdentity \rightarrow Repository \rightarrow DELETE \rightarrow MySQL }
例如:
$repository->delete(
15
);
Repository:
public function delete($id)
{
$sql = "
DELETE FROM individuals
WHERE id = :id
";
$statement =
$this->pdo->prepare($sql);
return $statement->execute(
array(':id' => $id)
);
}
形成:
ID→DELETE→RecordRemovedID \rightarrow DELETE \rightarrow RecordRemoved
242.16 删除与Domain Object
删除数据库记录不一定等于销毁 Domain Object。
必须区分:
DatabaseDeleteDatabaseDelete
与:
ObjectDestroyObjectDestroy
例如:
Domain Object
↓
Repository.delete()
↓
MySQL记录删除
此时数据库记录不存在了,但当前 PHP 请求中的 Domain Object 变量可能仍然存在。
因此:
DatabaseRecord=∅DatabaseRecord=\varnothing
不代表:
DomainObject=∅DomainObject=\varnothing
两者属于不同生命周期。
242.17 删除与ICAI对象生命周期
第159章建立了:
Create→Initialize→Load→Run→Modify→Save→Update→DestroyCreate \rightarrow Initialize \rightarrow Load \rightarrow Run \rightarrow Modify \rightarrow Save \rightarrow Update \rightarrow Destroy
数据库删除主要对应持久化层的:
DeleteDelete
而 Domain Object 销毁属于:
DestroyDestroy
因此:
Delete≠DestroyDelete\neq Destroy
例如 ICAI 机器个体被停用以后,可以选择:
active
↓
inactive
而不是直接:
DELETE
因此数据库删除必须根据具体领域规则决定。
242.18 软删除
对于某些 ICAI 对象,不适合直接删除数据库记录,可以采用状态标记。
例如:
deleted = 1
或者:
status = deleted
此时:
LogicalDeleteLogicalDelete
代替:
PhysicalDeletePhysicalDelete
例如:
UPDATE individuals
SET deleted = 1
WHERE id = :id
这样历史数据仍然保留。
因此:
DeleteDelete
可以进一步分为:
Delete=LogicalDelete+PhysicalDeleteDelete= LogicalDelete+ PhysicalDelete
具体方式取决于 Domain Object 的生命周期和历史保存要求。
242.19 Repository四种基本操作
Repository 最基本的数据操作可以统一为:
CRUD\boxed{ CRUD }
其中:
C=CreateC=Create R=ReadR=Read U=UpdateU=Update D=DeleteD=Delete
对应:
| 操作 | Repository | MySQL |
|---|---|---|
| 创建 | save | INSERT |
| 查询 | find/query | SELECT |
| 更新 | update | UPDATE |
| 删除 | delete | DELETE |
因此:
Repository→CRUD→MySQLRepository \rightarrow CRUD \rightarrow MySQL
242.20 Repository与Service
Service 不应该直接操作 MySQL。
错误:
Service
↓
SQL
↓
MySQL
推荐:
Service
↓
Repository
↓
MySQL
例如:
class IndividualService
{
protected $repository;
public function __construct(
IndividualRepository $repository
) {
$this->repository =
$repository;
}
public function find($id)
{
return $this->repository
->find($id);
}
}
因此:
Service→RepositoryService \rightarrow Repository
而:
Repository→MySQLRepository \rightarrow MySQL
242.21 Repository与Engine
Engine 通常也不应该直接操作 MySQL。
错误:
Engine
↓
SQL
↓
MySQL
推荐:
Service
↓
Repository
↓
Domain Object
↓
Engine
↓
Domain Object
↓
Repository
↓
MySQL
即:
Engine≠Repository\boxed{ Engine \neq Repository }
Engine 负责:
CalculationCalculation
Repository 负责:
PersistencePersistence
242.22 Repository与Domain Object双向转换
Repository 的重要职责之一是完成:
Record↔DomainObjectRecord\leftrightarrow DomainObject
查询:
MySQLRecord→Repository→DomainObjectMySQLRecord \rightarrow Repository \rightarrow DomainObject
保存:
DomainObject→Repository→MySQLRecordDomainObject \rightarrow Repository \rightarrow MySQLRecord
因此:
Repository=Mapping+Persistence\boxed{ Repository= Mapping+ Persistence }
例如:
MySQL:
id,name,type,state
↓ map
Domain Object:
Individual
反方向:
Domain Object:
Individual
↓ map
MySQL:
id,name,type,state
242.23 Repository查询完整流程
查询流程:
Request
↓
Controller
↓
Service
↓
Repository
↓
PDO
↓
MySQL
↓
Record
↓
Repository
↓
Domain Object
↓
Service
↓
Controller
数学形式:
ID→Repository→MySQL→Record→DomainObject\boxed{ ID \rightarrow Repository \rightarrow MySQL \rightarrow Record \rightarrow DomainObject }
这是数据库数据进入 ICAI 运行环境的基本入口。
242.24 Repository保存完整流程
保存流程:
Domain Object
↓
Service
↓
Repository
↓
PDO
↓
INSERT
↓
MySQL
↓
Insert ID
↓
Repository
↓
Domain Object
数学形式:
DomainObject→Repository→INSERT→MySQL\boxed{ DomainObject \rightarrow Repository \rightarrow INSERT \rightarrow MySQL }
如果产生新的 ID:
MySQL→Repository→DomainObject.idMySQL \rightarrow Repository \rightarrow DomainObject.id
242.25 Repository更新完整流程
更新流程:
Domain Object
↓
Engine计算
↓
状态修改
↓
Service
↓
Repository
↓
UPDATE
↓
MySQL
数学模型:
Objectt→Engine→Objectt+1→Repository→Databaset+1\boxed{ Object_t \rightarrow Engine \rightarrow Object_{t+1} \rightarrow Repository \rightarrow Database_{t+1} }
因此:
Objectt+1≈Databaset+1Object_{t+1} \approx Database_{t+1}
这里的“≈”表示领域对象状态与持久化状态完成同步,而不是两个对象在技术上完全相同。
242.26 Repository删除完整流程
删除流程:
Business Request
↓
Controller
↓
Service
↓
Repository
↓
DELETE
↓
MySQL
如果采用软删除:
Service
↓
Repository
↓
UPDATE deleted=1
↓
MySQL
因此删除的具体实现应该由业务规则决定,而不是简单地认为:
Delete=SQL DELETEDelete=SQL\ DELETE
242.27 MySQL表与Domain Object
例如机器个体 Domain Object:
class Individual
{
protected $id;
protected $name;
protected $type;
protected $state;
}
对应数据库:
CREATE TABLE individuals (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
type VARCHAR(100) NOT NULL,
state VARCHAR(100) NOT NULL,
created_at DATETIME NOT NULL,
updated_at DATETIME NOT NULL
);
形成:
Individual↔individualsIndividual \leftrightarrow individuals
但不能简单认为:
Individual=individualsIndividual=individuals
因为:
Individual=DomainObjectIndividual=DomainObject
而:
individuals=DatabaseTableindividuals=DatabaseTable
两者属于不同层次。
242.28 Repository接口
可以定义:
interface IndividualRepositoryInterface
{
public function find($id);
public function save(
Individual $individual
);
public function update(
Individual $individual
);
public function delete($id);
}
具体实现:
class MySQLIndividualRepository
implements IndividualRepositoryInterface
{
protected $pdo;
public function __construct(PDO $pdo)
{
$this->pdo = $pdo;
}
public function find($id)
{
// SELECT
}
public function save(
Individual $individual
) {
// INSERT
}
public function update(
Individual $individual
) {
// UPDATE
}
public function delete($id)
{
// DELETE
}
}
因此:
Service→RepositoryInterfaceService \rightarrow RepositoryInterface
而:
MySQLRepository→MySQLMySQLRepository \rightarrow MySQL
可以降低业务层与具体数据库访问实现之间的耦合。
242.29 PDO与MySQL
在 PHP 工程中,可以通过 PDO 访问 MySQL:
PHP→PDO→MySQLPHP \rightarrow PDO \rightarrow MySQL
例如:
$pdo = new PDO(
'mysql:host=localhost;dbname=icai;charset=utf8mb4',
'root',
'password'
);
然后:
$statement =
$pdo->prepare($sql);
执行:
$statement->execute(
$params
);
Repository 使用 PDO,而不是把 PDO 连接代码散布到 Service 和 Engine 中。
因此:
PDO连接属于Repository基础设施边界\boxed{ PDO连接属于Repository基础设施边界 }
242.30 数据查询异常
查询可能发生异常:
数据库连接失败
SQL执行失败
表不存在
字段不存在
参数错误
权限不足
因此:
Repository→PersistenceExceptionRepository \rightarrow PersistenceException
Repository 不应该静默返回错误数据。
错误:
try {
// query
} catch (Exception $e) {
return array();
}
这种处理会把:
DatabaseErrorDatabaseError
错误转换成:
EmptyDataEmptyData
Service 就无法区分:
NoDataNoData
和:
QueryFailedQueryFailed
正确做法是保持异常语义:
Repository→Exception→ServiceRepository \rightarrow Exception \rightarrow Service
242.31 查询为空与查询失败
必须严格区分:
NotFoundNotFound
和:
QueryFailedQueryFailed
例如:
数据库正常:
SELECT
↓
0 records
这是:
NotFoundNotFound
数据库异常:
SELECT
↓
Connection Error
这是:
PersistenceExceptionPersistenceException
因此:
NotFound≠QueryFailed\boxed{ NotFound\neq QueryFailed }
这是 Repository 工程的重要边界。
242.32 保存、更新与事务
如果一个业务过程涉及多个数据库操作,就可能需要事务。
例如:
保存Individual
↓
保存Capability
↓
保存Goal
↓
保存Memory
如果中间失败:
保存Individual ✓
保存Capability ✓
保存Goal ✗
则系统可能处于不完整状态。
因此:
Transaction=Begin→Operations→Commit/RollbackTransaction= Begin \rightarrow Operations \rightarrow Commit/Rollback
例如:
$pdo->beginTransaction();
try {
// INSERT
// INSERT
// UPDATE
$pdo->commit();
} catch (Exception $e) {
$pdo->rollBack();
throw $e;
}
事务属于持久化一致性机制。
242.33 Repository与状态一致性
Engine 修改 Domain Object 后:
StateObjectState_{Object}
发生变化。
如果没有保存:
StateDatabaseState_{Database}
仍然可能保持旧状态。
例如:
Domain Object:
running
MySQL:
idle
此时:
StateObject≠StateDatabaseState_{Object}\neq State_{Database}
因此:
ObjectChange→Persistence→Synchronization\boxed{ ObjectChange \rightarrow Persistence \rightarrow Synchronization }
Repository 的重要作用就是完成这种状态持久化。
242.34 Repository与ICAI记忆
ICAI 的记忆结构也需要持久化。
例如:
individual_memory
可以保存:
id
individual_id
memory_type
content
state
source
created_at
形成:
MemoryObject→MemoryRepository→MySQLMemoryObject \rightarrow MemoryRepository \rightarrow MySQL
同样:
Experience→ExperienceRepository→MySQLExperience \rightarrow ExperienceRepository \rightarrow MySQL Knowledge→KnowledgeRepository→MySQLKnowledge \rightarrow KnowledgeRepository \rightarrow MySQL
因此 Repository 不只是保存普通 CRUD 数据,也是 ICAI 长期运行的重要持久化基础。
242.35 Repository与机器个体生命周期
机器个体运行过程中会不断经历:
Load→Run→Modify→Save→UpdateLoad \rightarrow Run \rightarrow Modify \rightarrow Save \rightarrow Update
例如:
MySQL
↓
Repository.find()
↓
Domain Object
↓
Engine
↓
状态变化
↓
Repository.update()
↓
MySQL
最终:
Databaset→Objectt→Engine→Objectt+1→Databaset+1Database_t \rightarrow Object_t \rightarrow Engine \rightarrow Object_{t+1} \rightarrow Database_{t+1}
这形成机器个体的一次完整持久化运行周期。
242.36 Repository→MySQL完整工程模型
综合本章:
┌──────────────┐
│ MySQL │
└──────┬───────┘
↑↓
┌──────┴───────┐
│ Repository │
└──────┬───────┘
↑↓
┌──────┴───────┐
│ DomainObject │
└──────┬───────┘
↑↓
Engine
↑↓
Service
↑↓
Controller
核心数据库操作:
Query+Save+Update+Delete\boxed{ Query+Save+Update+Delete }
对应:
SELECT+INSERT+UPDATE+DELETE\boxed{ SELECT+INSERT+UPDATE+DELETE }
242.37 四种操作统一模型
可以将四种基本操作统一为:
查询
MySQL→Repository→DomainObjectMySQL \rightarrow Repository \rightarrow DomainObject
保存
DomainObject→Repository→MySQLDomainObject \rightarrow Repository \rightarrow MySQL
更新
DomainObjectnew→Repository→MySQLDomainObject_{new} \rightarrow Repository \rightarrow MySQL
删除
Identity→Repository→MySQLIdentity \rightarrow Repository \rightarrow MySQL
因此:
Repository=Read+Create+Update+Delete\boxed{ Repository= Read+ Create+ Update+ Delete }
242.38 本章核心边界
本章必须保持以下边界:
Repository≠MySQL\boxed{ Repository\neq MySQL } DomainObject≠DatabaseRecord\boxed{ DomainObject\neq DatabaseRecord } Service≠Repository\boxed{ Service\neq Repository } Engine≠Repository\boxed{ Engine\neq Repository } Delete≠Destroy\boxed{ Delete\neq Destroy } NotFound≠QueryFailed\boxed{ NotFound\neq QueryFailed } ObjectUpdate≠DatabaseUpdate\boxed{ ObjectUpdate\neq DatabaseUpdate }
对象首先发生领域变化:
DomainObjectt→DomainObjectt+1DomainObject_t \rightarrow DomainObject_{t+1}
然后才进行持久化:
DomainObjectt+1→Repository→MySQLDomainObject_{t+1} \rightarrow Repository \rightarrow MySQL
242.39 ICAI完整数据运行链
第239章到第242章可以形成完整工程链:
Controller→Service→Engine→DomainObject→Repository→MySQL\boxed{ Controller \rightarrow Service \rightarrow Engine \rightarrow DomainObject \rightarrow Repository \rightarrow MySQL }
具体过程:
HTTP Request
↓
Controller
↓
Service
↓
Repository 查询
↓
Domain Object
↓
Engine 计算
↓
Domain Object 修改
↓
State Update
↓
Repository 更新
↓
MySQL
↓
Business Result
↓
Controller
↓
Response
这已经构成一个完整的 ICAI 单次业务运行过程。
242.40 本章总结
Repository→MySQL 是 ICAI 工程体系中实现数据持久化的核心关系。
Repository 负责将 Domain Object 与数据库记录连接起来:
DomainObject↔Repository↔MySQL\boxed{ DomainObject \leftrightarrow Repository \leftrightarrow MySQL }
查询:
MySQL→Repository→DomainObject\boxed{ MySQL \rightarrow Repository \rightarrow DomainObject }
保存:
DomainObject→Repository→INSERT→MySQL\boxed{ DomainObject \rightarrow Repository \rightarrow INSERT \rightarrow MySQL }
更新:
DomainObjectnew→Repository→UPDATE→MySQL\boxed{ DomainObject_{new} \rightarrow Repository \rightarrow UPDATE \rightarrow MySQL }
删除:
Identity→Repository→DELETE→MySQL\boxed{ Identity \rightarrow Repository \rightarrow DELETE \rightarrow MySQL }
因此四种基本数据操作可以统一为:
Query+Save+Update+Delete\boxed{ Query+ Save+ Update+ Delete }
而 ICAI 完整运行链进一步形成:
Controller→Service→Engine→DomainObject→Repository→MySQL\boxed{ Controller \rightarrow Service \rightarrow Engine \rightarrow DomainObject \rightarrow Repository \rightarrow MySQL }
数据查询方向:
MySQL→Repository→DomainObject\boxed{ MySQL \rightarrow Repository \rightarrow DomainObject }
数据修改方向:
DomainObject→Repository→MySQL\boxed{ DomainObject \rightarrow Repository \rightarrow MySQL }
由此,前面三个工程关系:
Controller→ServiceController\rightarrow Service Service→EngineService\rightarrow Engine Engine→DomainObjectEngine\rightarrow DomainObject
与本章:
Repository→MySQLRepository\rightarrow MySQL
共同形成 ICAI MVC 工程中的完整数据与计算路径:
Request→Controller→Service→Engine→DomainObject→Repository→MySQL\boxed{ Request \rightarrow Controller \rightarrow Service \rightarrow Engine \rightarrow DomainObject \rightarrow Repository \rightarrow MySQL }
而从数据库重新进入机器个体运行环境,则形成反向数据加载:
MySQL→Repository→DomainObject→Engine→State\boxed{ MySQL \rightarrow Repository \rightarrow DomainObject \rightarrow Engine \rightarrow State }
至此,ICAI 已经形成从请求、业务、计算、对象、持久化到数据重新加载的基本闭环,为后续数据库关系、事务、数据一致性以及 ICAI Runtime 的持续运行奠定工程基础。
第242B章 统一持久化层接口语义
242B.1 提出背景
第242章已经建立:
Repository→MySQLRepository\rightarrow MySQL
并确定 Repository 负责数据查询、保存、更新和删除,将 Domain Object 与 MySQL 数据连接起来。
但是,当 ICAI 系统中的 Domain Object 不断增加以后,仅仅拥有 Repository 还存在一个新的问题。
例如:
IndividualRepository
GoalRepository
CapabilityRepository
MemoryRepository
BehaviorRepository
RiskRepository
如果每一个 Repository 都自行定义方法:
get()
load()
read()
find()
insert()
save()
store()
modify()
update()
remove()
delete()
虽然这些 Repository 都能够访问数据库,但是整个 ICAI 系统的持久化接口会逐渐失去统一性。
因此,在:
Repository→MySQLRepository\rightarrow MySQL
的基础上,还必须建立:
统一持久化层接口语义\boxed{ 统一持久化层接口语义 }
其核心不是规定所有 Repository 必须使用完全相同的数据库表结构,而是规定:
相同的持久化行为必须具有一致的接口名称、输入语义、输出语义、状态语义和异常语义。
因此形成:
DomainObject→RepositoryInterface→Repository→MySQLDomainObject \rightarrow RepositoryInterface \rightarrow Repository \rightarrow MySQL
242B.2 统一持久化层接口定义
统一持久化层接口(Unified Persistence Interface),是指 ICAI 对不同 Domain Object 的持久化操作建立统一的程序接口规范,使不同 Repository 对外表现出一致的基本操作语义。
可以定义:
PersistenceInterface={Create,Find,Save,Update,Delete}PersistenceInterface= \{ Create, Find, Save, Update, Delete \}
其中:
Create:建立新的持久化对象;Find:根据身份或条件获取对象;Save:保存对象当前状态;Update:更新已经存在的对象;Delete:删除持久化对象。
这里必须明确:
Interface≠ImplementationInterface\neq Implementation
接口规定:
应该提供什么持久化能力。
实现规定:
具体如何完成持久化。
242B.3 为什么需要统一接口
如果没有统一接口,不同 Repository 很容易出现:
IndividualRepository::find()
GoalRepository::get()
MemoryRepository::load()
BehaviorRepository::read()
这些方法虽然可能都表示“查询”,但是调用者必须记住每一个 Repository 的不同名称。
统一以后:
IndividualRepository::find()
GoalRepository::find()
MemoryRepository::find()
BehaviorRepository::find()
于是:
QuerySemantic=FindQuerySemantic=Find
调用者只需要理解一种基本查询语义。
因此:
统一接口→降低系统认知复杂度\boxed{ 统一接口\rightarrow降低系统认知复杂度 }
统一接口并不是为了减少代码,而是为了减少不同模块之间的语义差异。
242B.4 Repository接口的核心原则
统一持久化接口必须遵循三个基本原则。
第一:
接口统一接口统一
第二:
语义统一语义统一
第三:
实现可以不同实现可以不同
因此:
RepositoryInterface
↓
┌──────┼────────┐
↓ ↓ ↓
Individual Goal Memory
Repository Repository Repository
↓
MySQL
不同 Repository 可以拥有不同的领域对象、不同的表结构和不同的数据映射方式,但对外提供的基本持久化语义应该保持一致。
242B.5 Create语义
Create 表示:
建立一个新的持久化对象。
形式:
Create(O)→PersistentObjectCreate(O) \rightarrow PersistentObject
其中 OO 表示 Domain Object。
例如:
$repository->create($individual);
其基本过程为:
Domain Object
↓
身份检查
↓
数据映射
↓
INSERT
↓
获得持久化身份
↓
持久化完成
因此:
Create=NewObject+PersistenceCreate= NewObject+ Persistence
Create 的重点是:
对象此前不存在于当前持久化集合中。
242B.6 Find语义
Find 表示:
根据对象身份寻找已经存在的持久化对象。
例如:
$individual =
$repository->find($id);
其过程:
Identity→Repository→MySQL→Record→DomainObjectIdentity \rightarrow Repository \rightarrow MySQL \rightarrow Record \rightarrow DomainObject
如果找到:
Find(id)=DomainObjectFind(id)=DomainObject
如果没有找到:
Find(id)=nullFind(id)=null
因此:
NotFound\neqException\boxed{ NotFound\neqException }
正常查询没有找到对象,不一定是系统错误。
242B.7 Save语义
Save 表示:
将 Domain Object 当前状态保存到持久化层。
形式:
Save(O)→Persist(O)Save(O) \rightarrow Persist(O)
它关注的是:
CurrentDomainStateCurrentDomainState
而不是单纯的 SQL INSERT。
因此:
Save≠INSERTSave\neq INSERT
也不能简单定义:
Save=UPDATESave=UPDATE
具体采用 INSERT 还是 UPDATE,应由 Repository 的持久化状态规则决定。
因此 Save 是一种较高层的持久化语义,而不是某一条固定 SQL 语句的别名。
242B.8 Update语义
Update 表示:
将已经存在的持久化对象更新为当前 Domain Object 的状态。
形式:
Ot→Update→Ot+1O_t \rightarrow Update \rightarrow O_{t+1}
例如:
$individual->setState('running');
$repository->update(
$individual
);
其本质是:
DomainStatenew→PersistentStatenewDomainState_{new} \rightarrow PersistentState_{new}
因此:
Update⇒ExistingObjectUpdate\Rightarrow ExistingObject
如果对象根本不存在,则不能无条件把 update() 转换为 create()。
否则就会造成接口语义混乱。
242B.9 Delete语义
Delete 表示:
移除对象对应的持久化记录。
形式:
Identity→Delete→NotPersistedIdentity \rightarrow Delete \rightarrow NotPersisted
例如:
$repository->delete($id);
但是必须保持:
Delete≠Destroy\boxed{ Delete\neq Destroy }
Delete 属于持久化层。
Destroy 属于对象生命周期。
因此:
Repository::delete()
不能直接解释为:
DomainObject::destroy()
242B.10 五种接口的统一模型
统一接口可以表示为:
PersistenceInterface={Create,Find,Save,Update,Delete}\boxed{ PersistenceInterface= \{ Create, Find, Save, Update, Delete \} }
对应:
| 接口 | 语义 | 输入 | 结果 |
|---|---|---|---|
create() |
建立持久化对象 | Domain Object | 持久化结果 |
find() |
查找对象 | Identity | Domain Object / null |
save() |
保存当前状态 | Domain Object | 持久化结果 |
update() |
更新已有对象 | Domain Object | 持久化结果 |
delete() |
删除持久化记录 | Identity | 持久化结果 |
这五种操作构成统一持久化层的基础语义。
242B.11 RepositoryInterface
PHP 中可以建立统一接口:
interface RepositoryInterface
{
public function create($object);
public function find($id);
public function save($object);
public function update($object);
public function delete($id);
}
这里的接口不负责:
SQL
PDO
MySQL
它只负责定义:
ContractContract
即:
Repository 必须向上层提供什么能力。
242B.12 Domain Object作为接口输入
统一持久化接口应尽量以 Domain Object 作为主要输入。
例如:
$repository->save(
$individual
);
而不是:
$repository->save(
array(
'name' => 'Robot A',
'state' => 'running'
)
);
因为数组只表示数据:
Array=DataArray=Data
而 Domain Object 表示领域对象:
DomainObject=Identity+State+Behavior+RuleDomainObject=Identity+State+Behavior+Rule
因此:
RepositoryInput=DomainObject\boxed{ RepositoryInput=DomainObject }
能够保持持久化层与领域层之间的正确关系。
242B.13 Identity作为查询输入
查询对象时通常不需要整个 Domain Object。
例如:
$repository->find(1001);
其中:
1001=Identity1001=Identity
所以:
FindInput=IdentityFindInput=Identity
而:
SaveInput=DomainObjectSaveInput=DomainObject
这两个接口的输入语义不同。
因此不能为了形式统一而强行要求所有 Repository 方法都接受相同参数类型。
真正需要统一的是:
SemanticContractSemanticContract
而不是机械地统一所有参数。
242B.14 查询结果语义
find() 必须明确规定结果。
定义:
Find(id)={DomainObjectObjectExistsnullObjectNotFoundExceptionPersistenceFailureFind(id)= \begin{cases} DomainObject & ObjectExists\\ null & ObjectNotFound\\ Exception & PersistenceFailure \end{cases}
因此有三个状态:
找到
↓
DomainObject
没有找到
↓
null
持久化失败
↓
PersistenceException
这三种状态不能混在一起。
242B.15 NotFound语义
没有找到对象是一种正常的数据状态。
例如:
$object =
$repository->find(99999);
如果数据库中不存在:
id = 99999
则:
null
可以表示:
ObjectNotFoundObjectNotFound
而不是:
throw new Exception();
这样 Service 才能够根据实际业务决定:
对象不存在
↓
创建
↓
返回业务提示
↓
或者结束当前业务
242B.16 PersistenceException语义
如果数据库发生异常:
连接失败
SQL执行失败
权限错误
事务提交失败
数据库不可用
则属于:
PersistenceExceptionPersistenceException
例如:
class PersistenceException
extends Exception
{
}
这样可以形成:
NotFoundNotFound
与:
PersistenceFailurePersistenceFailure
的明确区别。
242B.17 BusinessException与PersistenceException
ICAI 系统需要区分:
BusinessExceptionBusinessException
和:
PersistenceExceptionPersistenceException
例如:
个体不存在
可能属于业务状态。
而:
MySQL连接失败
属于持久化系统异常。
因此:
BusinessException≠PersistenceException\boxed{ BusinessException\neq PersistenceException }
业务层不应该把数据库故障误认为业务条件不满足。
242B.18 PersistenceResult
对于 create()、save()、update()、delete() 等写操作,可以建立统一的结果对象。
例如:
class PersistenceResult
{
protected $success;
protected $affectedRows;
protected $id;
protected $message;
public function __construct(
$success,
$affectedRows = 0,
$id = null,
$message = ''
) {
$this->success =
$success;
$this->affectedRows =
$affectedRows;
$this->id = $id;
$this->message =
$message;
}
public function isSuccess()
{
return $this->success;
}
public function getAffectedRows()
{
return $this->affectedRows;
}
public function getId()
{
return $this->id;
}
public function getMessage()
{
return $this->message;
}
}
于是:
WriteOperation→PersistenceResultWriteOperation \rightarrow PersistenceResult
不同 Repository 可以共享这种结果语义。
242B.19 Create与Save的语义边界
Create 和 Save 不能简单视为同一个方法。
Create 强调:
New→PersistentNew \rightarrow Persistent
而 Save 强调:
CurrentState→PersistentStateCurrentState \rightarrow PersistentState
因此:
Create
重点是:
建立一个新持久化对象。
而:
Save
重点是:
保存对象当前状态。
如果一个系统只需要简单 CRUD,可以选择只保留 save();如果需要严格区分对象生命周期,则可以明确提供 create() 和 save()。
因此:
InterfaceSemanticInterfaceSemantic
必须根据 ICAI 的对象生命周期模型确定,而不能机械复制数据库 CRUD。
242B.20 Save与Update的语义边界
同样:
Save≠UpdateSave\neq Update
Update 明确表示:
ExistingObject→NewStateExistingObject \rightarrow NewState
而 Save 可以表示:
Object→PersistCurrentStateObject \rightarrow PersistCurrentState
因此:
Save
可以是更高层的持久化动作。
而:
Update
是明确的已有对象更新动作。
242B.21 Repository接口与数据库实现
统一接口:
RepositoryInterface
具体实现:
MySQLRepository
形成:
RepositoryInterface←MySQLRepositoryRepositoryInterface \leftarrow MySQLRepository
例如:
class IndividualRepository
implements RepositoryInterface
{
protected $pdo;
public function __construct(PDO $pdo)
{
$this->pdo = $pdo;
}
public function create($object)
{
// INSERT
}
public function find($id)
{
// SELECT
}
public function save($object)
{
// SAVE
}
public function update($object)
{
// UPDATE
}
public function delete($id)
{
// DELETE
}
}
这样:
Interface≠ImplementationInterface \neq Implementation
242B.22 不同Repository统一接口
例如:
IndividualRepository
GoalRepository
CapabilityRepository
MemoryRepository
BehaviorRepository
RiskRepository
都可以遵循统一接口。
形成:
RepositoryInterface
↓
┌───────────────┼───────────────┐
↓ ↓ ↓
Individual Goal Memory
Repository Repository Repository
↓ ↓ ↓
└────────────── MySQL ──────────┘
不同 Repository 的领域对象不同,但基础持久化语义保持一致。
242B.23 Domain Object与Repository边界
必须保持:
DomainObject≠Repository\boxed{ DomainObject\neq Repository }
Domain Object 表示:
领域中的对象。
Repository 表示:
对象如何进入和离开持久化系统。
例如:
Individual
负责表达:
ID
Type
State
Attribute
Relation
而:
IndividualRepository
负责:
Find
Create
Save
Update
Delete
因此:
DomainObject=DomainRepresentationDomainObject=DomainRepresentation Repository=PersistenceAccessRepository=PersistenceAccess
242B.24 Repository与Engine边界
同样:
Engine≠Repository\boxed{ Engine\neq Repository }
Engine 负责:
CalculationCalculation
Repository 负责:
PersistencePersistence
因此:
Engine
↓
计算对象状态
↓
Domain Object
↓
Repository
↓
MySQL
而不是:
Engine
↓
SQL
↓
MySQL
这保证领域计算与数据库实现分离。
242B.25 Repository与Service边界
Service 负责业务流程:
Service=BusinessFlowService=BusinessFlow
Repository 负责持久化:
Repository=PersistenceRepository=Persistence
因此:
Service
↓
Repository Interface
↓
Repository
例如:
public function updateState(
$id,
$state
) {
$individual =
$this->repository->find($id);
if ($individual === null) {
return null;
}
$individual->setState($state);
return $this->repository
->update($individual);
}
Service 不需要知道:
SELECT
UPDATE
PDO
MySQL
242B.26 Repository事务接口语义
当一次业务操作需要修改多个对象时,需要事务。
可以定义:
Transaction=Begin+Commit+RollbackTransaction= Begin+Commit+Rollback
例如:
Begin
↓
Repository A
↓
Repository B
↓
Repository C
↓
Commit
发生错误:
Begin
↓
Repository A
↓
Repository B
↓
Error
↓
Rollback
事务的目的不是统一 Repository 方法,而是统一:
PersistenceConsistencyPersistenceConsistency
242B.27 事务与Service边界
一次完整业务可能调用多个 Repository:
Service
↓
Begin
├── IndividualRepository
├── GoalRepository
├── MemoryRepository
└── BehaviorRepository
↓
Commit
因此业务事务通常由 Service 组织。
原因是:
Service=BusinessTransactionService=BusinessTransaction
而:
Repository=PersistenceOperationRepository=PersistenceOperation
Repository 不应该自行决定整个业务流程何时提交或回滚。
242B.28 持久化接口与对象状态
Domain Object 在内存中的状态:
StateObjectState_{Object}
数据库中的状态:
StateDatabaseState_{Database}
对象发生变化后:
StateObject,t→StateObject,t+1State_{Object,t} \rightarrow State_{Object,t+1}
再通过:
RepositoryRepository
完成:
StateDatabase,t→StateDatabase,t+1State_{Database,t} \rightarrow State_{Database,t+1}
因此:
ObjectChange→Persistence→SynchronizationObjectChange \rightarrow Persistence \rightarrow Synchronization
242B.29 ICAI记忆对象的统一持久化
ICAI 的 Memory 同样遵循统一接口。
例如:
$memoryRepository->find($id);
$memoryRepository->save($memory);
$memoryRepository->update($memory);
$memoryRepository->delete($id);
因此:
MemoryObject→MemoryRepository→MySQLMemoryObject \rightarrow MemoryRepository \rightarrow MySQL
知识、经验、行为、目标等对象也可以采用相同持久化语义。
例如:
Knowledge→KnowledgeRepositoryKnowledge \rightarrow KnowledgeRepository Experience→ExperienceRepositoryExperience \rightarrow ExperienceRepository Behavior→BehaviorRepositoryBehavior \rightarrow BehaviorRepository
242B.30 ICAI统一持久化模型
综合前面的定义:
UnifiedPersistence=Interface+DomainObject+Repository+Result+Exception+Transaction\boxed{ UnifiedPersistence= Interface+ DomainObject+ Repository+ Result+ Exception+ Transaction }
其运行结构为:
Domain Object
↓
Repository Interface
↓
Repository Implementation
↓
Persistence
↓
MySQL
查询方向:
MySQL
↓
Repository
↓
Domain Object
写入方向:
Domain Object
↓
Repository
↓
MySQL
242B.31 与第242章的关系
第242章解决的是:
Repository→MySQL\boxed{ Repository\rightarrow MySQL }
重点是:
- 数据查询;
- 数据保存;
- 数据更新;
- 数据删除;
- PDO;
- MySQL;
- 数据映射;
- 持久化状态。
本章解决的是:
RepositoryInterface→Repository\boxed{ RepositoryInterface\rightarrow Repository }
重点是:
- 接口;
- 方法语义;
- 输入语义;
- 输出语义;
- 空结果;
- 异常;
- 持久化结果;
- 事务语义。
因此两者不是重复关系。
完整关系为:
RepositoryInterface→Repository→MySQLRepositoryInterface \rightarrow Repository \rightarrow MySQL
242B.32 ICAI完整持久化链
经过第242章和第242B章,完整结构成为:
Controller
↓
Service
↓
Engine
↓
Domain Object
↓
Repository Interface
↓
Repository
↓
PDO
↓
MySQL
查询时:
MySQL
↓
PDO
↓
Repository
↓
Domain Object
↓
Engine
↓
Service
↓
Controller
因此:
Request→Controller→Service→Engine→DomainObject→RepositoryInterface→Repository→MySQL\boxed{ Request \rightarrow Controller \rightarrow Service \rightarrow Engine \rightarrow DomainObject \rightarrow RepositoryInterface \rightarrow Repository \rightarrow MySQL }
242B.33 统一接口的工程意义
统一持久化接口使 ICAI 获得以下能力。
第一,降低调用复杂度
不同 Repository 使用统一基本语义:
Find/Save/Update/DeleteFind/Save/Update/Delete
第二,隔离数据库实现
Service⊥MySQLService \perp MySQL
第三,统一错误处理
NotFoundNotFound
与:
PersistenceExceptionPersistenceException
明确区分。
第四,统一结果结构
PersistenceResultPersistenceResult
第五,支持 Repository 替换
Interface→ImplementationInterface \rightarrow Implementation
第六,支持 ICAI 大量 Domain Object 的统一持久化
Individual→RepositoryIndividual \rightarrow Repository Memory→RepositoryMemory \rightarrow Repository Goal→RepositoryGoal \rightarrow Repository Behavior→RepositoryBehavior \rightarrow Repository
242B.34 统一持久化层最终模型
最终可以建立:
PersistenceLayer=Interface+Repository+Mapping+Result+Exception+Transaction+Storage\boxed{ PersistenceLayer= Interface+ Repository+ Mapping+ Result+ Exception+ Transaction+ Storage }
完整关系:
DomainObject→RepositoryInterface→Repository→PDO→MySQL\boxed{ DomainObject \rightarrow RepositoryInterface \rightarrow Repository \rightarrow PDO \rightarrow MySQL }
其中:
Create=建立新的持久化对象Create=\text{建立新的持久化对象} Find=查找已有对象Find=\text{查找已有对象} Save=保存对象当前状态Save=\text{保存对象当前状态} Update=更新已有对象Update=\text{更新已有对象} Delete=删除持久化记录Delete=\text{删除持久化记录}
查询结果:
Find→DomainObject/nullFind\rightarrow DomainObject/null
写操作:
Write→PersistenceResultWrite\rightarrow PersistenceResult
异常:
PersistenceFailure→PersistenceExceptionPersistenceFailure\rightarrow PersistenceException
事务:
Begin→Operation→Commit/RollbackBegin\rightarrow Operation\rightarrow Commit/Rollback
242B.35 本章总结
第242章建立了:
Repository→MySQLRepository\rightarrow MySQL
本章进一步建立:
RepositoryInterface→Repository\boxed{ RepositoryInterface\rightarrow Repository }
二者共同形成:
DomainObject→RepositoryInterface→Repository→MySQL\boxed{ DomainObject \rightarrow RepositoryInterface \rightarrow Repository \rightarrow MySQL }
统一持久化层的核心并不是要求所有 Repository 使用完全相同的数据库结构,而是要求相同的持久化行为具有稳定、明确、可预测的接口语义。
因此形成:
Create+Find+Save+Update+Delete\boxed{ Create+ Find+ Save+ Update+ Delete }
同时明确:
Repository≠MySQLRepository\neq MySQL DomainObject≠DatabaseRecordDomainObject\neq DatabaseRecord Engine≠RepositoryEngine\neq Repository Service≠RepositoryService\neq Repository Delete≠DestroyDelete\neq Destroy NotFound≠PersistenceExceptionNotFound\neq PersistenceException
最终,ICAI 的工程链从:
Controller→Service→Engine→DomainObject→Repository→MySQLController \rightarrow Service \rightarrow Engine \rightarrow DomainObject \rightarrow Repository \rightarrow MySQL
进一步规范为:
Controller→Service→Engine→DomainObject→RepositoryInterface→Repository→Persistence→MySQL\boxed{ Controller \rightarrow Service \rightarrow Engine \rightarrow DomainObject \rightarrow RepositoryInterface \rightarrow Repository \rightarrow Persistence \rightarrow MySQL }
由此,持久化层不再只是“访问数据库的 PHP 类”,而成为 ICAI 领域对象与长期数据存储之间具有统一接口、统一语义、统一结果和统一异常边界的独立工程层。
这为后续第243章继续建立新的 ICAI 工程关系提供统一的持久化基础。