PROJECT OVERVIEW ================ Document Purpose ---------------- This document is a technical architecture review of the Laravel + Vue + Inertia vehicle marketplace project. It is intended for another developer, team lead, or architect who needs to understand how the system is structured today, where the major domain boundaries are, what has already been implemented, and where the current risks and refactoring opportunities exist. 1. PROJECT SUMMARY ------------------ System description - This application is a role-based vehicle marketplace supporting three main actor types: admin, seller, and customer. - It combines inventory management, dynamic vehicle specifications, catalog-driven make/model configuration, public vehicle search, and a purchase flow that includes orders, payments, and shipment milestones. - The project has evolved from a simple vehicle CRUD system into a hybrid marketplace platform with an EAV-style vehicle attribute architecture layered on top of a conventional relational schema. High-level product responsibilities - Admins manage the global catalog, listings, sellers, and orders. - Sellers create and maintain vehicle inventory. - Customers browse publicly available vehicles, maintain carts, create purchases, and track payment/shipping progress. Technology stack - PHP 8.2 - Laravel 12 - Laravel Jetstream - Laravel Fortify - Laravel Sanctum - Inertia.js for Laravel - Vue 3 - Tailwind CSS - Vite - MySQL as the target production database Architectural style - Backend: monolithic Laravel application with service-layer helpers for filtering and catalog metadata. - Frontend: server-driven SPA using Inertia.js and Vue single-file components. - Data model: hybrid relational + EAV approach. Strategic observation - The most important architectural shift in this codebase is the move from fixed vehicle columns toward a catalog-driven specification model. The codebase still preserves compatibility with older vehicle fields, but the dynamic attribute system is now central to forms, filters, and catalog behavior. 2. DATABASE STRUCTURE --------------------- The database can be understood in five functional layers: - identity and access - vehicle inventory - dynamic catalog metadata - commerce and payment flow - platform and infrastructure tables 2.1 Identity and access tables ------------------------------ users - Purpose: stores all authenticated principals in the system. - Main roles: admin, seller, customer. - Key columns: - id - name - email - phone - address - country - role - password - email_verified_at - current_team_id - two_factor_secret and related Fortify fields - Relationships: - one-to-many to vehicles through seller_id - one-to-many to orders - one-to-many to carts - one-to-many to payment_histories teams, team_user, team_invitations - Purpose: Jetstream team support. - Current role in product behavior: secondary infrastructure, not the main authorization model. personal_access_tokens - Purpose: Sanctum token storage. 2.2 Vehicle inventory tables ---------------------------- vehicles - Purpose: the main listing table. - Current design note: this is both a domain table and a compatibility boundary. It stores normalized foreign keys to makes/models while also retaining legacy string-based fields such as make, model, vehicle_type, color, transmission, and fuel_type. - Key columns: - id - seller_id - make_id - model_id - rec_no - make - model - chassis - grade - registration_year - manufacture_year - year - mileage - price - discount_percentage - discount_amount - is_featured - is_clearance - condition - vehicle_type - color - transmission - fuel_type - engine_capacity - length_m - width_m - height_m - volume_m3 - seats - doors - location - stock_country - primary_image - stock_status - status - features_json - Relationships: - belongs to users as seller - belongs to makes - belongs to models - has many vehicle_images - has many vehicle_documents - has many vehicle_attribute_values - belongs to many attributes through vehicle_attribute_values - referenced by carts, order_items, shipments, and document flows vehicle_images - Purpose: stores additional image paths for vehicles. - Key columns: - id - vehicle_id - image_path - Relationships: - belongs to vehicles vehicle_documents - Purpose: stores vehicle-level documents used during operations and delivery workflows. - Key columns: vehicle_id plus document metadata fields. - Relationships: - belongs to vehicles 2.3 Dynamic catalog metadata tables ----------------------------------- makes - Purpose: manufacturer master data. - Key columns: - id - name - Relationships: - one-to-many to models - one-to-many to vehicles - many-to-many to attributes through make_attributes models - Purpose: model master data scoped to a make. - Key columns: - id - make_id - name - Relationships: - belongs to makes - one-to-many to vehicles - many-to-many to attributes through model_attributes - one-to-many to model_attribute_defaults attributes - Purpose: master definition of dynamic specification fields. - Key columns: - id - name - type - Supported types: - select - multi_select - range - boolean - Relationships: - one-to-many to attribute_values - many-to-many to models through model_attributes - many-to-many to makes through make_attributes - one-to-many to vehicle_attribute_values attribute_values - Purpose: value dictionary for select and multi-select attributes. - Key columns: - id - attribute_id - value - Relationships: - belongs to attributes - optionally referenced by vehicle_attribute_values - optionally referenced as defaults in model_attributes and make_attributes model_attributes - Purpose: defines which attributes apply to a model and what metadata those attributes carry. - Key columns: - id - model_id - attribute_id - min_numeric_value - max_numeric_value - min_value - max_value - default_value_id - default_raw_value - default_value - Relationships: - belongs to models - belongs to attributes - Architectural role: - controls which attributes appear on the vehicle form for a given model - stores allowed ranges for numeric attributes - stores default values and allowed select options via JSON payloads make_attributes - Purpose: make-level fallback attribute metadata when no model-specific override is defined. - Key columns: - id - make_id - attribute_id - min_value - max_value - default_value_id - default_raw_value - Relationships: - belongs to makes - belongs to attributes vehicle_attribute_values - Purpose: per-vehicle EAV storage for dynamic specifications. - Key columns: - id - vehicle_id - attribute_id - value_id - raw_value - Relationships: - belongs to vehicles - belongs to attributes - optionally belongs to attribute_values - Usage pattern: - select and multi-select: value_id is primary - range and boolean: raw_value is primary model_attribute_defaults - Purpose: secondary default storage for model attribute defaults. - Key columns: - id - model_id - attribute_id - value_id - raw_value - Relationships: - belongs to models - belongs to attributes - optionally belongs to attribute_values 2.4 Commerce and order flow tables ---------------------------------- orders - Purpose: top-level customer purchase record. - Key columns: - id - user_id - status - total_price - currency - type - discount_amount - paid_amount - remaining_balance - min_ship_deposit_percentage - shipping_cost - shipping_cost_reason - tracking_number - shipping_address - from_port - arrival_port - notes - order_date - buy_date - shipped_at - departure_date - delivered_at - arrival_date - dhl_date - customer_status - documents - Relationships: - belongs to users - has many order_items - has many payments - has many shipments - has many payment_allocations - has many payment_histories - Evolution note: - the original migration included vehicle_id, but later schema changes removed it in favor of order_items as the source of vehicle linkage. order_items - Purpose: connects ordered vehicles to orders. - Key columns: - id - order_id - vehicle_id - quantity - price - discount_applied - Relationships: - belongs to orders - belongs to vehicles payments - Purpose: payment transaction records for orders. - Key columns: - id - order_id - payment_method - transaction_id - amount - status - payment_data - paid_at - Relationships: - belongs to orders - has many payment_allocations payment_allocations - Purpose: allocation layer for distributing received payments to orders. - Key columns: - id - payment_id - order_id - amount - Relationships: - belongs to payments - belongs to orders payment_histories - Purpose: customer ledger-style payment history records. - Key columns: - id - user_id - order_id - currency - received_amount - allocated_amount - balance_amount - payment_type - payment_method - transaction_id - notes - receive_date - Relationships: - belongs to users - belongs to orders shipments - Purpose: shipment milestone tracking per ordered vehicle. - Key columns: - id - order_id - vehicle_id - arrival_port - buy_date - ship_ok_date - departure_date - arrival_date - status - Relationships: - belongs to orders - belongs to vehicles carts - Purpose: pre-checkout customer vehicle selections. - Key columns: - id - user_id - vehicle_id - quantity - Relationships: - belongs to users - belongs to vehicles 2.5 Supporting and infrastructure tables ---------------------------------------- - cache and jobs related tables support framework-level operations. - personal_access_tokens supports Sanctum. - team tables come from Jetstream. 3. MODEL RELATIONSHIPS ---------------------- Primary domain graph Vehicle -> Make - Vehicle belongs to Make through make_id. - Vehicle also mirrors make into a string field for compatibility and display. Vehicle -> Model - Vehicle belongs to VehicleModel through model_id. - Vehicle also mirrors model into a string field. Vehicle -> Attribute Values - Vehicle has many VehicleAttributeValue rows. - Vehicle belongs to many VehicleAttribute rows through vehicle_attribute_values. - This is the central dynamic specification storage model. Model -> Attributes - VehicleModel belongs to many VehicleAttribute rows through model_attributes. - model_attributes contains both the mapping itself and the metadata needed for forms and filters. Attribute -> Values - VehicleAttribute has many AttributeValue rows. - These are only applicable to select and multi-select types. Make -> Attributes - Make belongs to many VehicleAttribute rows through make_attributes. - This allows broader make-level defaults and range constraints when model-level detail is absent. Order flow relationships - Order belongs to User. - Order has many OrderItem. - OrderItem belongs to Vehicle. - Order has many Payment. - Payment has many PaymentAllocation. - Order has many Shipment. Important compatibility behavior in the Vehicle model - Vehicle resolves deprecated fields like vehicle_type, color, transmission, and fuel_type through dynamic attribute values first, then falls back to legacy columns. - Engine capacity is similarly synchronized from the dynamic attribute named Engine CC. - This makes Vehicle a translation layer between the old schema shape and the newer catalog/EAV design. 4. CORE FEATURES IMPLEMENTED ---------------------------- Vehicle CRUD - Admin vehicle create, edit, show, delete - Seller vehicle create, edit, show, delete with ownership checks Dynamic attribute system - Catalog attributes support select, multi-select, range, and boolean types - Model-specific attribute definitions drive vehicle forms - Vehicle values are stored in vehicle_attribute_values - Defaults are carried via model metadata and model_attribute_defaults Make and model system - Admin can manage makes and models - Vehicles reference normalized make_id and model_id - Make-level fallback attributes exist for broader catalog behavior Filter system - Dynamic public filters generated from current dataset - Support for grouped select, multi-select, range, and boolean filters - Make/model facets and stock country facets - Range facets for price, mileage, year, and dynamic range attributes Feature system - features_json stores vehicle equipment/features - Human-readable feature labels are derived from config/vehicle_features.php - The Vehicle model exposes feature_labels for all consuming UIs UI work already implemented - Public listing redesigned with a top search bar and grouped sticky sidebar - Admin and seller list pages use pagination and server-side filters - Public listing follows a marketplace-focused, catalog-heavy browse pattern inspired by BE FORWARD style layouts Commerce flow - Cart support - Checkout and purchase creation - Orders, payments, payment history, and shipment milestones - Customer document access for vehicle/order related files 5. FILTER SYSTEM ---------------- Filter architecture - The central service is VehicleFilterService. - Controllers pass request parameters to normalize(). - The normalized filter payload is then used to build: - listing queries - aggregate/facet queries - min/max range calculations Primary filter inputs supported - make_id - model_id - search - min_price / max_price - min_mileage / max_mileage - year_from / year_to - stock_country - vehicle_type - attribute_filters for dynamic attributes How listing queries work - buildListingQuery() creates the core vehicle query. - It starts from vehicles filtered to stock_status = available. - A published_only option restricts public inventory to published listings. - Search is applied across make, model, chassis, and grade. - Standard field ranges are applied directly to vehicles columns. - Dynamic attribute filters are applied through joined vehicle_attribute_values aliases. Select filter behavior - For dynamic select and multi-select attributes, the filter joins vehicle_attribute_values and filters by value_id. - Facet counts are computed by grouping on facet_values.value_id. Range filter behavior - Static ranges exist for price, mileage, and year. - Dynamic range attributes use raw_value and SQL casts for comparisons. - Facet min/max values are computed from current result sets, excluding the active filter for that attribute. Boolean filter behavior - Boolean attributes use raw_value with true/false represented as 1 or 0. - Facet logic counts positive values and disables filters that would return no results. Grouping and deduplication logic - Attributes can be assembled from both make-level and model-level sources. - VehicleCatalogAttributeService merges those sources. - Both VehicleCatalogAttributeService and VehicleFilterService perform attribute deduplication by attribute_id. - Public/Home.vue also normalizes and groups attributes before rendering. Architectural observation - The filter system is one of the stronger parts of the codebase because the public listing, catalog routes, and aggregate queries all depend on a centralized query builder rather than scattered custom SQL. 6. ADMIN PANEL -------------- Implemented admin capabilities - Dashboard statistics - Vehicle management - Seller listing - Attribute CRUD - Attribute value CRUD for select-based attributes - Make CRUD - Model CRUD - Model attribute mapping and defaults - Vehicle document management - Order visibility and update operations Vehicle management architecture - Admin vehicle listing applies search, seller filter, status filter, and sort on the server. - Results are paginated. - Vehicle create/edit forms supply makes, locations, stock countries, feature options, stock statuses, and dynamic attribute metadata. - Admin can assign listings to sellers. Admin dashboard behavior - Dashboard counts sellers, vehicles, availability, featured, and clearance inventory. - Vehicle type statistics now derive from the dynamic Body Type attribute instead of relying only on the legacy vehicle_type column. Current admin limitations - Dashboard analytics remain operational, not strategic. There is no advanced BI layer for conversion, stock aging, seller performance, or payment throughput. - Vehicle listing query logic is controller-local rather than extracted into shared reusable inventory services. - No visible bulk moderation or batch operations exist for vehicles or catalog data. 7. SELLER PANEL --------------- Implemented seller capabilities - Seller dashboard with aggregated counts - Seller vehicle CRUD with ownership enforcement - Seller inventory listing with server-side filtering, sorting, and pagination - Seller vehicle document management - Seller order visibility and update access Vehicle creation flow - Seller chooses make and model - Model selection loads dynamic attributes - Form submits static vehicle fields plus serialized dynamic_attributes payload - Vehicle model syncs EAV values and mirrored compatibility columns Inventory management behavior - Search covers make, model, year, rec_no, and chassis - Status filtering supports available, reserved, and sold concepts - Sorting supports newest, make, low price, and high price Architectural note - Seller and admin inventory pages are parallel implementations. They solve similar problems with near-duplicate controller logic, which is a future refactor candidate. 8. PUBLIC LISTING ----------------- Public browse architecture - PublicController builds the result list using VehicleFilterService. - VehicleCatalogController exposes supporting catalog metadata endpoints for models and dynamic attribute facets. - Public/Home.vue consumes the server-provided listing and filter metadata. Search bar behavior - Prominent top search interface - Supports text search, make, model, price, and year controls Sidebar behavior - Sticky desktop sidebar - Grouped into Basic Filters, Specifications, and Features - Dynamic attributes rendered once per attribute_id - Active filter chips support removing filters individually Pagination behavior - Public inventory uses paginate(12) with query preservation - The UI uses Inertia navigation rather than Blade paginator rendering Public detail page behavior - Vehicle detail page loads images and dynamic attribute relations - Human-readable feature labels are shown through the Vehicle model accessor Architectural note - The public listing is the area where the dynamic catalog architecture is most visible. It is effectively a query-driven marketplace catalog, not just a simple vehicle table browser. 9. PERFORMANCE OPTIMIZATIONS ---------------------------- Pagination - Public vehicle listing paginates - Admin vehicle listing paginates - Seller vehicle listing paginates - Seller dashboard recent vehicles paginate - Query parameters are preserved across pagination to maintain filter state Eager loading - Public listing loads images and attribute relations - Public detail loads images and attribute relations - Admin and seller list/detail pages load seller, images, and attribute relations where appropriate - API vehicle listing also loads seller and dynamic attribute relations Query design improvements already visible in code - Filter construction is centralized in VehicleFilterService - Aggregate range queries are separated from listing queries - Distinct vehicle counting is used where join duplication could distort counts - Catalog metadata endpoints use caching per make/model in VehicleCatalogController - GROUP BY compatibility fixes were applied for MySQL strict mode where computed expressions were grouped incorrectly by alias Indexing and schema support - vehicles has make_id/model_id indexing - vehicle_attribute_values has vehicle_id and attribute/value indexes - This is important because the EAV layer would otherwise degrade filter performance quickly 10. CURRENT ISSUES / LIMITATIONS -------------------------------- The following items are visible in the current codebase and should be treated as real architectural observations. 10.1 Dual-schema complexity - The codebase still carries a hybrid between fixed columns and dynamic attributes. - This is deliberate, but it increases mental overhead. - Developers must understand which source of truth is primary in each context. 10.2 Duplicated inventory query logic - Admin and seller vehicle controllers both contain nearly identical filtering, counting, sorting, and pagination code. - This increases maintenance cost and creates drift risk. 10.3 Duplicated attribute deduplication logic - Attribute deduplication appears in VehicleCatalogAttributeService and VehicleFilterService. - Some frontend normalization also repeats that logic. - The behavior is correct, but the rule is implemented in more than one place. 10.4 Split catalog/API surface - Public catalog metadata comes from web routes handled by VehicleCatalogController. - Form-specific model attribute/default data also comes from dedicated API routes. - The result is workable but not fully unified as a single catalog API surface. 10.5 Schema evolution is hard to infer from one migration - The order flow especially requires reading multiple migrations to understand the final design. - Example: orders originally had vehicle_id, but later moved to order_items as the true vehicle association. - This can confuse maintainers unless documentation like this exists. 10.6 Test environment mismatch - The codebase behavior and migrations are clearly oriented toward MySQL. - Earlier validation showed test failures under SQLite in-memory mode due to missing tables and unsupported foreign key alteration behavior. - This is not only a test failure issue; it is a maintainability concern because local test confidence is weaker than it should be. 10.7 Likely bug in admin vehicle update image handling - In Admin\VehicleController::update(), the current code unsets primary_image immediately after the upload branch instead of only when no file is present. - That likely prevents primary image updates from persisting in the admin update flow. 10.8 Residual compatibility coupling - Several parts of the system still check legacy columns directly, even though dynamic attributes are intended to be authoritative. - The project has already improved this for body type and feature labels, but the pattern remains a long-term coupling cost. 11. NEXT IMPROVEMENTS (AUTO-DETECTED) ------------------------------------- Priority improvements from an architectural perspective: 11.1 Extract inventory query services - Create a shared service or query object for admin and seller inventory listings. - This should own search fields, stock/status interpretation, sorting, pagination defaults, and counter generation. 11.2 Reduce dual-source truth for vehicle specifications - Continue shifting consumers to dynamic attributes as the authoritative catalog source. - Treat legacy fields as derived compatibility outputs only. 11.3 Consolidate catalog metadata APIs - Consider a dedicated catalog API layer with clear separation between: - public browse metadata - admin catalog management metadata - form-specific model defaults and constraints 11.4 Improve automated tests around the dynamic catalog - High-value missing tests likely include: - make/model dependent attribute loading - filter facet generation - pagination with preserved filters - vehicle dynamic attribute synchronization - compatibility column mirroring 11.5 Add richer operational analytics - Recommended future admin metrics: - stock aging - seller conversion rates - order funnel stages - payment collection status - shipment throughput 11.6 Introduce explicit state transition rules for orders - Order, payment, and shipment flows currently exist, but stronger domain services or state machines would reduce accidental invalid transitions. 11.7 Add bulk operations and moderation tooling - Batch publishing, clearance updates, and seller moderation workflows would improve operational efficiency. 11.8 Improve caching strategy documentation and invalidation - Catalog routes already use caching, but future work should define where cache invalidation must occur after make/model/attribute updates. 12. KEY SERVICES AND THEIR RESPONSIBILITIES ------------------------------------------- VehicleFilterService - Central filtering and facet engine. - Normalizes request filters. - Builds listing queries. - Builds aggregate queries for makes, models, stock countries, and attribute facets. VehicleCatalogAttributeService - Assembles make-level and model-level attribute definitions. - Merges metadata and deduplicates attributes. - Supplies model insights such as observed price ranges. VehicleFormOptionsService - Provides normalized lists of locations and stock countries for forms. Vehicle model - Acts as a compatibility adapter. - Resolves feature labels. - Synchronizes dynamic attributes into legacy fields where required. 13. ROUTING AND APPLICATION BOUNDARIES -------------------------------------- Public routes - Home listing - Public vehicle detail - Public catalog metadata endpoints for makes/models/attributes - Payment webhook endpoint Authenticated role routes - Admin namespace for dashboards, users, catalog management, vehicles, documents, and orders - Seller namespace for dashboard, vehicles, documents, and orders - Customer namespace for dashboard, cart, checkout, orders, documents, and payments API routes - Model attribute endpoint - Model defaults endpoint Architectural observation - This is a classic role-segmented monolith. The route structure is understandable, but the catalog-related routes are split across web and api namespaces, which is one reason the API surface feels uneven. 14. SENIOR ARCHITECT ASSESSMENT ------------------------------- Overall maturity assessment - The project is significantly more capable than a basic Laravel CRUD app. - The dynamic catalog and filter system show meaningful product architecture work. - The codebase already contains the foundations of a real marketplace domain model. Strengths - Clear business roles and route segmentation - Strong centralization of public filter/query logic - Thoughtful transition from fixed schema to dynamic catalog metadata - Good use of Eloquent relationships for domain modeling - Public listing experience is aligned with catalog-heavy marketplace browsing Weaknesses - Some domain logic still lives in controllers where shared services would be safer - The hybrid schema increases complexity and requires careful documentation - Testing confidence appears lower than the application complexity now warrants - There are still a few signs of iterative growth rather than deliberate final consolidation Architectural conclusion - The project is in a solid intermediate architecture state. - It already has enough structure to support continued growth, but it has reached the point where consolidation and refactoring will produce outsized benefits. - The next major quality gains will come less from adding new features and more from unifying duplicated logic, strengthening tests, and reducing compatibility-era complexity. 15. RECOMMENDED ONBOARDING PATH FOR A NEW DEVELOPER --------------------------------------------------- Suggested order of study: 1. routes/web.php and routes/api.php 2. app/Models/Vehicle.php 3. app/Models/Make.php, VehicleModel.php, VehicleAttribute.php, VehicleAttributeValue.php 4. app/Services/VehicleFilterService.php 5. app/Services/VehicleCatalogAttributeService.php 6. app/Http/Controllers/PublicController.php and VehicleCatalogController.php 7. app/Http/Controllers/Admin/VehicleController.php and Seller/VehicleController.php 8. app/Models/Order.php, OrderItem.php, Payment.php, Shipment.php 9. Vue pages under resources/js/Pages/Public, Admin, and Seller If a developer understands those files first, they will understand the majority of the system’s business and technical behavior.