Pokemon
Platform with detailed information on each Pokémon, including statistics, abilities, moves, evolutions, and unique features
Below, I present the technical and architectural analysis of this Pokedex project, designed to serve simultaneously as the repository’s official documentation and as a case study. My primary focus is to describe the system design, architectural decisions, and data flows in an analytical and objective manner.
1. Utility and General Operation
The application is designed to solve the problem of indexing, searching, and visualizing complex data of “Pokémon” entities. Its primary utility lies in consolidating scattered information (statistics, evolution lines, moves, physical descriptions) into a cohesive and high-fidelity visual user interface.
The main operational workflow occurs as follows:
- Initialization: Upon mounting the web application, the client requests a base state synchronized with the current URL. If no parameters are defined, the state self-synchronizes with the initial identifier (ID 1) and requests the first paginated segment of the general directory.
- Data Resolution: Queries are sent via GraphQL operations to the backend server. This service acts as an intermediary to an in-memory database pre-seeded with consolidated information, ensuring minimal latency.
- State Processing: Network responses mutate an immutable global state (NgRx Signals) on the client.
- Reactive Update: Presentation components, which are agnostic to network logic, receive the new data (properties of the selected Pokémon and the pagination grid) and update the DOM accordingly, reflecting graphical elements such as models and base statistics.
2. Architecture General and Data Flows
I designed the system using an approach that combines Clean Architecture and Domain-Driven Design (DDD) principles on the backend, along with a Container/Presenter pattern (Smart/Dumb Components) on the frontend. The separation of concerns ensures high maintainability and scalability.
System Topology
The following diagram illustrates the interaction between the distinct domains of the system:
graph TD
subgraph Frontend [Client - Angular 19]
UI_Layout[Layout/Smart Components]
UI_Dumb[Presentational Components]
Store[NgRx Signal Store]
Service[GraphQL Service / Apollo]
UI_Layout -->|Dispatches Actions| Store
Store -->|Provides Reactive State| UI_Layout
UI_Layout -->|Injects Data| UI_Dumb
Store -->|Invokes Methods| Service
end
subgraph Backend [Server - NestJS]
Resolver[GraphQL Resolver]
AppService[Application Service]
Repository[In-Memory Repository]
Entities[Domain Entities]
Service -->|GraphQL Queries| Resolver
Resolver -->|Dependency Injection| AppService
AppService -->|Domain Calls| Repository
Repository -.->|Mapping| Entities
end
subgraph Data [Initial Persistence Layer]
Seed[Seed Data JSON]
PokeAPI[External PokeAPI]
Repository -.->|Initial Hydration| Seed
Seed -.->|Previous Cache| PokeAPI
end
Backend Architecture
The NestJS server implements a layered abstraction (Domain-Driven Design):
- Presentation Layer (Resolvers):
PokemonResolverdefines the GraphQL contract (auto-generated schema) and manages input objects (filters, pagination). - Application Layer (Services):
PokemonServiceorchestrates use cases, such as individual retrieval and fuzzy search of entities. - Infrastructure Layer (Repositories):
InMemoryPokemonRepositoryimplements actual persistence mechanisms. In this topology, in-memory storage is used based on a pre-generated seed dataset. - Domain Layer (Entities): Classes like
Pokemon,PokemonStats, andPokemonMovedefine the core business.
Frontend Architecture
The Angular 19 client uses the NgRx Signals model for highly predictable and performant state management:
PokemonStorecentralizes global state. It uses reactive flows (RxJS) internally for the defragmentation of asynchronous events (such as search debounce) before patching the Signal-based state.- Components are strictly divided between those handling layout/state and those handling pure rendering (Glassmorphism UI).
3. Data Modeling and Business Logic
The main entity, Pokemon, encapsulates the complexity of the associated data.
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
Repository search logic is resolved by applying chained filtering operations at the server level, first evaluating whether the input query intercepts the name or numeric index, and subsequently narrowing down the results according to an optional type filter, before resolving the sub-array corresponding to the pagination of the current view.
4. Technology Stack
The technology stack was selected based on strong typing and event-oriented, data-flow architectures.
| Technology | Category | Technical Role in the Architecture |
|---|---|---|
| Angular 19 | Frontend Framework | Reactive rendering engine; component lifecycle and routing synchronized with the state. |
| NgRx Signals | State Management | Global state manager mutating reactive atomic signals; reduces the complexity of the standard digestion cycle. |
| Apollo Client | Data Fetching | GraphQL client with optimized cache policy strategy (e.g., cache-and-network). |
| Tailwind CSS v4 | UI Styling | Utility CSS engine under PostCSS architecture, structuring the Glassmorphism layer without rigid component libraries. |
| NestJS | Backend Framework | Modular structuring through Inversion of Control (IoC), supporting enterprise design patterns. |
| GraphQL | API Layer | Strongly typed query interface, mitigating over-fetching of data from complex models. |
| Biome | Toolchain | Unified tool for code formatting and linting, guaranteeing transversal syntactic consistency. |
5. Architectural Closure
The consolidation of the Container/Presenter pattern in the frontend along with a GraphQL-to-DDD in the backend eliminates ambiguity in data contracts and effectively isolates state mutations from UI side effects. The incorporation of asynchronous abstractions for in-memory mapping allows the system to respond with pre-calculated high availability, simplifying server orchestration and focusing processing on the reactive manipulation of Signals at the client level.