Pokemon
包含每只宝可梦详细信息的平台,涉及属性统计、特性、招式、进化链等。
下面,我展示了这个 Pokedex 项目的技术与架构分析,该项目旨在同时作为仓库的官方文档和案例研究。我的核心重点是以分析和客观的方式描述系统设计、架构决策和数据流。
1. 业务实用性与通用工作流
该应用旨在解决“宝可梦 (Pokémon)”实体复杂数据的索引、搜索和可视化问题。其主要实用价值在于将分散的信息(属性统计、进化链、招式、物理描述)整合到一个具有高视觉保真度的凝聚用户界面中。
主要业务工作流按以下方式运行:
- 初始化: 在挂载 Web 应用时,客户端请求与当前 URL 同步的基础状态。如果没有定义参数,状态会自动与初始标识符(ID 1)同步,并请求通用目录的第一个分页片段。
- 数据解析: 查询通过 GraphQL 操作发送到后端服务器。该服务充当预先种子的内存数据库的中间件,该数据库包含合并信息,确保极低延迟。
- 状态处理: 网络响应改变客户端中不可变的全局状态 (NgRx Signals)。
- 响应式更新: 呈现组件(与网络逻辑无关)接收新数据(选定宝可梦的属性和分页网格)并相应地更新 DOM,反映诸如模型和基础属性等图形元素。
2. 整体架构与数据流
我设计的系统结合了后端 Clean Architecture 和 Domain-Driven Design (DDD) 原则,以及前端的容器/呈现者模式 (Smart/Dumb Components)。职责分离确保了高可维护性和可扩展性。
系统拓扑
下图说明了系统不同领域之间的交互:
graph TD
subgraph Frontend [客户端 - Angular 19]
UI_Layout[Layout/Smart 组件]
UI_Dumb[Presentational 组件]
Store[NgRx Signal Store]
Service[GraphQL Service / Apollo]
UI_Layout -->|派发动作| Store
Store -->|提供响应式状态| UI_Layout
UI_Layout -->|注入数据| UI_Dumb
Store -->|调用方法| Service
end
subgraph Backend [服务端 - NestJS]
Resolver[GraphQL Resolver]
AppService[Application Service]
Repository[In-Memory 仓库]
Entities[Domain 实体]
Service -->|GraphQL 查询| Resolver
Resolver -->|依赖注入| AppService
AppService -->|领域调用| Repository
Repository -.->|映射| Entities
end
subgraph Data [初始持久化层]
Seed[Seed Data JSON]
PokeAPI[External PokeAPI]
Repository -.->|初始水化| Seed
Seed -.->|预缓存| PokeAPI
end
后端架构
NestJS 服务端实现了分层抽象 (Domain-Driven Design):
- 呈现层 (Resolvers):
PokemonResolver定义了 GraphQL 契约(自动生成的 Schema)并管理输入对象(过滤器、分页)。 - 应用层 (Services):
PokemonService编排用例,如单个实体的检索和模糊搜索。 - 基础设施层 (Repositories):
InMemoryPokemonRepository实现真正的持久化机制。在这种拓扑结构中,使用的是基于先前生成的种子数据集的内存存储。 - 领域层 (Entities): 诸如
Pokemon、PokemonStats和PokemonMove等类定义了业务核心。
前端架构
Angular 19 客户端使用 NgRx Signals 模型来实现高度可预测和高性能的状态管理:
PokemonStore集中管理全局状态。它在内部使用响应式流 (RxJS) 来消除异步事件的碎片(如搜索的 debounce),然后更新基于 Signals 的状态。- 组件严格划分为负责布局/状态的组件和负责纯渲染的组件 (Glassmorphism UI)。
3. 数据建模与业务逻辑
主要实体 Pokemon 封装了相关数据的复杂性。
classDiagram
class Pokemon {
+String id
+Number number
+String name
+String[] types
+String description
+PokemonPhysic physic
+PokemonStats stats
+PokemonEvolution evolution
+PokemonMove[] moves
}
class PokemonStats {
+Number health
+Number attack
+Number defense
+Number resistance
+Number speed
}
class PokemonMove {
+String name
+String type
+Number damage
}
class PokemonPhysic {
+Number weight
+Number height
}
class PokemonEvolution {
+String[] pre
+String[] pos
}
Pokemon *-- PokemonStats
Pokemon *-- PokemonMove
Pokemon *-- PokemonPhysic
Pokemon *-- PokemonEvolution
仓库中的搜索逻辑是通过在服务端应用链式过滤操作来解决的,首先评估输入 query 是否截获名称或数字索引,随后根据可选的类型过滤器过滤结果,最后解析对应于当前视图分页的子数组。
4. 技术栈
技术选型基于强类型以及面向事件和数据流的架构。
| 技术 | 类别 | 在架构中的技术角色 |
|---|---|---|
| Angular 19 | 前端框架 | 响应式渲染引擎;组件生命周期与状态同步路由。 |
| NgRx Signals | 状态管理 | 全局状态管理器,改变响应式原子信号;降低标准消化循环的复杂性。 |
| Apollo Client | 数据拉取 | 具有优化缓存策略的 GraphQL 客户端(例如 cache-and-network)。 |
| Tailwind CSS v4 | UI 样式 | PostCSS 架构下的实用程序 CSS 引擎,构建 Glassmorphism 层而无需僵化的组件库。 |
| NestJS | 后端框架 | 通过控制反转 (IoC) 进行模块化构建,支持企业级设计模式。 |
| GraphQL | API 层 | 强类型查询接口,减少复杂数据模型的 over-fetching。 |
| Biome | Toolchain | 用于代码格式化和 linting 的统一工具,确保横向语法一致性。 |
5. 架构总结
前端容器/呈现者模式的巩固与后端的 GraphQL-to-DDD 架构相结合,清除了数据契约中的模糊性,并有效地将状态突变与 UI 中的副作用隔离。结合异步抽象进行内存映射,使系统能够以预先计算的高可用性响应,简化了服务端编排,并将处理集中在客户端层面的 Signals 响应式操作上。
拖动可移动图示。按 + 和 − 缩放,方向键移动,0 重置视图。
一起合作
软件工程师、高级全栈开发工程师,常驻玻利维亚Santa Cruz,远程办公。欢迎新的工作机会。