Product Comparison Engine
Relevant source files
The following files were used as context for generating this wiki page:
The Product Comparison Engine is a core system within the Product Compare platform designed to facilitate collaborative and AI-assisted evaluation of different products. It allows users to select multiple products (currently capped at 3) and view their specifications, brands, and descriptions side-by-side. The engine is built as part of a Phoenix modular monolith, leveraging a React-based frontend with Relay-ready boundaries to handle data fetching and state management.
Sources: README.md, ARCHITECTURE.md:10-15, assets/src/routes/compare/api.ts:54-61
System Architecture and Data Flow
The comparison engine operates through a coordinated flow between the frontend route loaders and the backend GraphQL API. Users initiate a comparison by providing product "slugs" via URL query parameters. The system then fetches detailed data for each product to build a comparative view.
Comparison Initialization Flow
The process begins with the compareLoader, which parses the URL for selected product slugs. It enforces a limit of three products to ensure a manageable UI and efficient data fetching.
The diagram above shows the logic within the compareLoader used to determine the state of the comparison route based on input parameters.
Sources: assets/src/routes/compare/api.ts:46-95, assets/src/routes/compare/tests/compare.route.test.tsx:55-145
Data Models and Types
The engine relies on specific TypeScript interfaces to maintain type safety during comparison operations.
| Interface | Description | Key Fields |
|---|---|---|
CompareRouteLoaderData | Sum type representing the possible states of the comparison page. | status, slugs, products |
ProductDetail | Represents the essential data for a product in a comparison list. | id, name, slug, brandName |
CreateSavedComparisonSetInput | Input structure for persisting a comparison. | name, productIds |
Sources: assets/src/routes/compare/api.ts:16-43, assets/src/routes/products/api.ts:7-13
Comparison Lifecycle and Persistence
Beyond simple viewing, the engine supports saving comparison sets for future reference. This involves a specialized GraphQL mutation and frontend state management to handle the submission and response.
Saving Comparison Sets
When a user clicks "Save comparison," the system generates a descriptive name (e.g., "Product A vs Product B") and sends the product IDs to the backend.
This sequence illustrates the asynchronous process of persisting a user's selected products into a permanent comparison set.
Sources: assets/src/routes/compare/index.tsx:11-38, assets/src/routes/compare/api.ts:97-106
Mutation Logic
The createSavedComparisonSet function handles the raw GraphQL response, normalizing potential errors into a standard CompareMutationError format. This ensures that the UI can consistently display error messages regardless of whether they originated from the GraphQL resolver or the network layer.
Sources: assets/src/routes/compare/api.ts:133-181, assets/src/routes/compare/tests/compare.route.test.tsx:199-241
Backend Context Boundaries
The comparison engine is supported by several backend contexts defined in the Phoenix modular monolith. These boundaries ensure that product specifications, taxonomy, and pricing data are handled by specialized modules.
ProductCompare.Specs: Manages attributes, units, and claims. It is responsible for fast filtering and numeric normalization (value_num_base).ProductCompare.Catalog: Anchors brands and products, serving as the primary source for product identity.ProductCompare.Taxonomy: Handles product types and use-case tagging, which informs how products are categorized for comparison.
Sources: README.md, 2026-03-03-product-compare-backend-mvp-plan.md:9-12
Implementation Details
Querying Product Details
The engine utilizes a specific GraphQL query to retrieve fields necessary for the side-by-side view.
query ProductDetail($slug: String!) {
product(slug: $slug) {
id
name
slug
description
brand {
id
name
}
}
}
Sources: assets/src/routes/products/api.ts:33-45
UI States
The CompareRoute component renders different views based on the status returned by the loader:
- Empty: Prompt to choose products.
- Too Many: Alert that only 3 products can be compared.
- Not Found: Alert that one or more products are missing from the catalog.
- Error: Generic failure message.
- Ready: Displays a list of product articles with names, brands, and descriptions.
Sources: assets/src/routes/compare/index.tsx:40-77
The Product Comparison Engine integrates the frontend React Router navigation with a GraphQL backend to provide a specialized view for evaluating products. By utilizing deterministic loaders and strict status-based rendering, the system provides a predictable experience for comparing complex product data.