System Architecture Overview
Relevant source files
The following files were used as context for generating this wiki page:
- ARCHITECTURE.md
- backend_starting_guide.md
- 2026-03-03-product-compare-backend-mvp-plan.md
- README.md
- AGENTS.md
- assets/src/relay/fetch-graphql.ts (Referenced via tests and integration docs)
The ProductCompare system is designed as a modular monolith, utilizing a Phoenix-based backend and a React-based frontend. The project emphasizes strict architectural boundaries, leveraging Elixir's context patterns for the backend and a Relay-ready GraphQL interface for communication. The core purpose of the system is to provide a collaborative platform for typed product comparisons, featuring taxonomy management, specification claims, and pricing history.
Sources: ARCHITECTURE.md:8-10, 2026-03-03-product-compare-backend-mvp-plan.md:7-9, README.md:3-5
High-Level Component Interaction
The architecture consists of two primary tiers: a Phoenix backend that acts as the source of truth for data and session authority, and a frontend built with React and Bun that handles the user experience.
This diagram illustrates the request flow from the React frontend through the GraphQL API to the Elixir domain logic and PostgreSQL database.
Sources: ARCHITECTURE.md:8-10, backend_starting_guide.md:20-30
Backend Architecture: Modular Monolith
The backend is organized into distinct context boundaries to ensure maintainability and clear separation of concerns. It utilizes Elixir/Phoenix and adheres to a "Relay-first" GraphQL implementation.
Domain Contexts
The business logic is encapsulated within specific boundaries:
| Context | Responsibility |
|---|---|
| Accounts | User management, reputation, and authentication. |
| Taxonomy | Tree structures for product types and use-cases using closure tables. |
| Catalog | Core product anchors and brand management. |
| Specs | Attribute definitions, unit conversions, and claim lifecycles. |
| Pricing | Merchant listings, price points, and historical tracking. |
| Affiliate | Network management, affiliate links, and coupons. |
| Discussions | Product threads, posts, and user reviews. |
Sources: 2026-03-03-product-compare-backend-mvp-plan.md:9-11, README.md:21-27
Data Persistence and Schema Rules
The system enforces strict rules for data modeling using Ecto and PostgreSQL 18:
- Primary Keys: Uses
bigintfor internal primary keys (id). - External Identifiers: Every relational table includes an
entropy_id(UUIDv7) with a unique index for external exposure. - Timestamps: Microsecond precision (
:utc_datetime_usec) is used globally. - Tokens: Token secrets are never stored raw; they are persisted as SHA3-256 hashes.
Sources: backend_starting_guide.md:10-18, backend_starting_guide.md:110-115
GraphQL and Authentication Contract
Communication between the frontend and backend is strictly performed via GraphQL at the /api/graphql endpoint.
The sequence diagram shows the authentication flow where Phoenix remains the cookie-backed session authority while exposing mutations through GraphQL.
Key API Implementation Details
- Relay Support: The schema uses
Absinthe.Relayto provide global IDs, node interface lookups, and connection-based pagination. - Dataloader: Implemented at the request level to batch database queries for associations like pricing and catalog details.
- Authorization: Priorities are given to
Authorization: Bearerheaders, with a session fallback for browser-based requests.
Sources: ARCHITECTURE.md:25-28, backend_starting_guide.md:75-85, backend_starting_guide.md:140-145
Frontend Architecture
The frontend is built as a Single Page Application (SPA) using React and Vite, integrated with Relay for data fetching.
Technical Stack
- Runtime/Bundler: Bun and Vite.
- State Management: React Relay (v20.1.1) with a centralized Network Layer.
- Styling: StyleX for type-safe CSS-in-JS.
- Routing: React Router v7 with SSR (Server-Side Rendering) support.
Sources: assets/package.json:20-45, ARCHITECTURE.md:10-12
Data Fetching Strategy
While the system is migrating toward a fully unified Relay approach, the current architecture handles data in a hybrid manner:
- Preloaded Queries: The SSR environment creates a Relay environment per request.
- Manual Fetching: Some routes currently use a
fetchGraphQLhelper that manages credentials and forwards SSR cookies to the backend. - Global IDs: The frontend consumes Relay-style global IDs for catalog and pricing surfaces to facilitate future
node(id:)refetching.
Sources: ARCHITECTURE.md:31-38, assets/src/relay/tests/fetch-graphql.test.ts:35-45
System Constraints and Gates
The architecture includes several "non-negotiable" verification gates to ensure code quality and consistency:
| Gate | Tooling | Description |
|---|---|---|
| Type Checking | Dialyzer / mix typecheck | Enforces @spec on public functions and schema types. |
| Precommit | mix precommit | Hard gate including formatting, tests, and compilation with warnings as errors. |
| Frontend Tests | Vitest / Playwright | Covers unit tests and end-to-end browser auth flows. |
| Database Sandbox | SQL Sandbox | Ensures test isolation using the DataCase owner pattern. |
Sources: backend_starting_guide.md:175-185, assets/package.json:7-13, README.md:31-34
Summary
The ProductCompare architecture provides a robust foundation for a data-intensive product platform. By utilizing a Phoenix modular monolith with strict context boundaries, the system ensures that business logic is decoupled from transport layers. The integration of GraphQL Relay and PostgreSQL 18's UUIDv7 support aligns the project with modern standards for performance, scalability, and developer experience.
Sources: ARCHITECTURE.md:3-5, 2026-03-03-product-compare-backend-mvp-plan.md:7-12