# 代码实践 沿登录走一遍。目的是看见页面、入口文件、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'`。新增仓储时,把实现文件加进对应的入口,并保证被路由加载到的页面引用了这个入口。