51 lines
2.8 KiB
Markdown
51 lines
2.8 KiB
Markdown
# 代码实践
|
||
|
||
沿登录走一遍。目的是看见页面、入口文件、ViewModel、用例、端口和网络怎样接在一起。分层见 [DDD是什么](./DDD是什么.md),容器见 [DI是什么](./DI是什么.md)。
|
||
|
||
## 目录
|
||
|
||
- [入口文件先执行](#入口文件先执行)
|
||
- [页面取出 ViewModel](#页面取出-viewmodel)
|
||
- [用例只依赖端口](#用例只依赖端口)
|
||
- [实现负责请求](#实现负责请求)
|
||
- [少了入口 import 会怎样](#少了入口-import-会怎样)
|
||
|
||
## 入口文件先执行
|
||
|
||
`src/features/security/security.ts` 没有导出,只有两行 import:
|
||
|
||
```ts
|
||
import './i18n';
|
||
import './infra/persistence/security_repository';
|
||
```
|
||
|
||
第一行执行 `registerMessages`,登录页的错误文案才能按键取到。第二行执行 `SecurityRepository` 上的 `@bindAs(ISecurityRepository)`,容器才知道端口用哪个类。
|
||
|
||
登录页第一行是 `import '../../security'`。这行不使用任何导出,作用是让上面两个文件运行。
|
||
|
||
## 页面取出 ViewModel
|
||
|
||
`security.tsx` 是默认导出的页面,由 `src/app/routes.ts` 挂在 `/security`,并放在 `guest` 布局里。已登录用户进不了这个路径。
|
||
|
||
页面是 `observer`,用 `useViewModel(SecurityViewModel)` 取出 ViewModel。用户提交时调用 `submit`。ViewModel 是 `Transient`,这次填写的账号和错误只留在这一次打开的页面里。
|
||
|
||
## 用例只依赖端口
|
||
|
||
`submit` 在登录页校验 `AuthCredentials`。通过后调用:
|
||
|
||
```ts
|
||
await this.application.login(this.loginForm);
|
||
```
|
||
|
||
`SecurityApplication.login` 不写 axios。它调用 `ISecurityRepository.login`,拿到 `LoginResult` 后执行 `tokenStore.save(result.accessToken)`。凭证怎样通知界面,由 `src/app/session` 负责。
|
||
|
||
## 实现负责请求
|
||
|
||
`SecurityRepository.login` 把实体收成快照,再调用 `this.http.instance.post('/auth/login', plainDto)`。响应收成 `User` 和 `LoginResult` 后返回给用例。`Http` 会在请求上附带已有凭证,并处理 401。这些步骤不写进用例。
|
||
|
||
## 少了入口 import 会怎样
|
||
|
||
页面如果没有 `import '../../security'`,`security.ts` 不会执行。文案没有注册,`ISecurityRepository` 也没有绑定。`useViewModel` 创建 `SecurityViewModel` 时,容器无法提供 `SecurityApplication` 所依赖的仓储。
|
||
|
||
项目模块同样依赖 `src/features/project/project.ts`。它导入本地和远程两个仓储,两者分别用 `@bindAs(IProjectRepository, ProjectStore.LOCAL)` 和 `ProjectStore.REMOTE` 绑到同一个端口。用例用 `@inject` 和 `@named` 注入对应实现。首页和编辑器在文件顶部写 `import '@/features/project/project'`,本地列表和远程列表写 `import '../../project'`。新增仓储时,把实现文件加进对应的入口,并保证被路由加载到的页面引用了这个入口。
|