Parts Catalog
Design a product compatibility system for an eCommerce site that lets users find which parts, accessories, or components are compatible with their specific product model, covering catalog modeling, search, and cross-referencing at millions of SKUs.
What is a parts compatibility catalog?
A parts compatibility catalog lets users find which parts fit their specific product (vehicle, appliance, or device model). The engineering challenge is not storing parts data; that part is easy.
It is the many-to-many compatibility relationship: a single part can fit thousands of vehicle configurations, and each vehicle can have thousands of compatible parts. Naive modeling creates hundreds of millions of fitment rows; smart attribute-based rules collapse that to a few million rule rows while still answering exact compatibility queries in under 100ms. I'd open an interview with exactly that framing because it signals you understand the real problem.
Functional Requirements
Core Requirements
- Users can search for parts compatible with a specific vehicle (year, make, model, submodel).
- Product pages show a "fits your vehicle" badge when a user's garage vehicle is set.
- Catalog managers can add, update, and remove fitment data.
- Results show only parts confirmed compatible with the exact vehicle configuration.
Below the Line (out of scope)
- Inventory management and stock levels
- Pricing and checkout
- Full-text search across part descriptions
The hardest part in scope: Fitment modeling. Explicitly storing one row per part-vehicle fitment pair for an automotive catalog produces hundreds of millions of rows. Attribute-based fitment rules collapse this dramatically while remaining queryable with a simple composite index lookup.
Inventory management is below the line because stock levels change independently of compatibility data. To add it: join the parts table with a separate inventory service and expose availability as a distinct API call. Keeping them decoupled prevents inventory writes from contending with the high-read compatibility path.
Pricing is below the line because it belongs to a separate pricing microservice with a part_id lookup. The catalog service stays read-only and does not need to understand promotional tiers or regional pricing rules.
Full-text search is below the line because it requires a dedicated search index. To add it: index part text fields into Elasticsearch, back the search service with the catalog database as source of truth, and run the two services independently. The catalog database is still the authoritative record; Elasticsearch is a derived read model.
Non-Functional Requirements
Core Requirements
- Scale: 10 million distinct parts, 300 million fitment relationships (or 5 million attribute-based fitment rules). 50 million DAU browsing products.
- Latency: Compatibility check returns under 100ms p99 end-to-end. Catalog updates reflected within 5 minutes.
- Availability: 99.9% uptime. Availability over consistency: a 5-minute lag on new fitment data is acceptable; returning wrong compatibility results is not.
- Read/write skew: Product page loads trigger compatibility checks far more than catalog managers add fitment data. The read path is the hot path.
Below the Line
- Sub-10ms compatibility checks (achievable with more aggressive caching but adds operational cost)
- Multi-region catalog replication
Read/write ratio: Every product page load triggers at least one compatibility check (read) for users with a saved garage vehicle. Catalog managers write fitment data in bulk imports, at most a few times per week. The read-to-write ratio is roughly 1,000:1. This makes the cache tier the most important component in the design, not the database. Almost every architectural decision in this article traces back to this number.
Core Entities
- Part: A catalog entry for a specific component or accessory (part_id, name, brand, part_number, category).
- Vehicle: A specific vehicle configuration (vehicle_id, year, make, model, submodel, engine).
- FitmentRule: The compatibility bridge between a Part and a set of Vehicles (either an explicit part_id + vehicle_id row, or an attribute-based rule row covering year_start, year_end, make, model, submodel).
- UserGarage: A user's saved vehicles (user_id, vehicle_id, nickname).
Schema design (column types, indexes, partitioning) is deferred to the deep dives. I'd keep the entity list this concise in an interview. These four entities are sufficient to drive the API design and High-Level Design.
API Design
FR1 + FR4: Search compatible parts for a vehicle:
GET /parts/compatible?year=2019&make=Ford&model=F-150&submodel=XLT&category=brake_pads
Response: { parts: [{ part_id, name, brand, part_number }], total, next_cursor }
Cursor-based pagination over offset: with 10 million parts, offset pagination requires scanning and discarding rows. Cursor-based pagination is O(1) per page regardless of depth.
FR2: Check "fits your vehicle" for a specific part:
GET /parts/{part_id}/fits?vehicle_id=veh_382
Response: { fits: true, notes?: "Fits 2019-2021 F-150 XLT with 5.0L engine" }
This endpoint is the hot path. It fires on every product page load for users with a saved garage vehicle. It must complete under 10ms at the application layer to stay within the 100ms end-to-end budget. A direct DB query here is a trap.
FR3: Add fitment data (catalog manager / admin):
POST /fitment
Body: { part_id, rule: { year_start, year_end, make, model, submodel } }
Response: { fitment_id }
Use rule (attribute-based) as the default in the API contract, not an explicit vehicle_id. This forces catalog managers to think in rules from day one. If a single vehicle_id is needed, the rule degenerates to year_start = year_end = specific_year with exact make, model, submodel values.
High-Level Design
1. Basic compatibility search (FR1 and FR4)
The read path is the system: a compatibility query must resolve to the correct list of compatible parts in under 100ms, even at 50 million DAU.
On the naive approach, a catalog manager stores one row per part-vehicle combination in a fitments table. A compatibility query selects all part_ids where vehicle_id matches. This works fine at thousands of parts.
Components (naive):
- Client: Web or mobile app. Sends a compatibility search with year, make, model, submodel.
- Catalog Service: Translates query params into a vehicle_id, queries fitments, returns the matching part list.
- PostgreSQL (fitments): Stores part_id + vehicle_id rows. An index on vehicle_id makes the lookup fast at small scale.
Request walkthrough (naive):
- Client sends
GET /parts/compatible?year=2019&make=Ford&model=F-150&submodel=XLT. - Catalog Service resolves the vehicle_id from the query parameters.
- Catalog Service queries:
SELECT part_id FROM fitments WHERE vehicle_id = 'veh_382'. - Catalog Service fetches part details for the returned part_id list.
- Response returns to the client.
At 300 million fitment rows, the index scan for one vehicle still returns thousands of part_id rows across a massive index tree. Query time climbs past 200ms at peak concurrent load. The database becomes the bottleneck before the application server does.
The fix is a cache. Pre-compute the compatible parts list for each vehicle_id and store it in Redis as a Set. A compatibility check becomes a single SMEMBERS call rather than a DB scan at every page load. I'd draw this cache addition on the whiteboard immediately after sketching the naive version.
Components (evolved):
- Redis (inverted index): One Redis Set per vehicle_id containing the compatible part_ids. Cache-aside pattern: populate on first miss, 24-hour TTL.
- PostgreSQL: Source of truth for cache misses. Not the hot path anymore.
Request walkthrough (evolved):
- Client sends the compatibility query.
- Catalog Service resolves the vehicle_id.
- Catalog Service calls
SMEMBERS vehicle:veh_382on Redis. - On cache hit (expected 95%+), return the part list immediately without touching the DB.
- On cache miss, query PostgreSQL, populate the Redis Set, return the result.
Redis absorbs the read load. The database handles only cache misses and writes, which is exactly what you want from a 1,000:1 read-skewed system.
2. "Fits your vehicle" badge on product pages (FR2)
Product page compatibility checks are the hottest request in the system: every product page load for a logged-in user with a saved garage vehicle fires this check.
Continue Reading with Premium
Unlock this article and every other in-depth system design guide on the platform with NotesFromSDE Premium.