Skip to content

Island Boundaries, Package Policy, and Performance Baseline

Purpose

Define strict guardrails so the migration stays modular without becoming fragmented into dozens of services/bundles/packages, while meeting performance needs for:

  • ~285,000 products
  • ~20,000 vehicles with fitment mappings
  • ~50 suppliers (stock/price imports)
  • ~60 platforms (orders/stock/tracking/invoices)
  • Composite wheel sets without native SKU

Decision Summary

  1. Use a small fixed island set (max 5 deployables in Day 1, including existing ERP middleware).
  2. Do not create a service per supplier/platform. Use a connector plugin model inside integration islands.
  3. Keep shared code to max 3 internal Composer packages; avoid shared domain-entity bundles across islands.
  4. Use precomputed read models for stock, fitment, and wheel sets; do not compute these from raw tables on every storefront request.
  5. Prefer asynchronous integration flows (queues + outbox) and synchronous APIs only for low-latency reads.

Day 1 Deployable Topology

Deployable Scope Why Separate
webshop Catalog read APIs, cart/checkout, orders, customers, admin Core transactional domain and storefront latency
stock-import Supplier file/API ingestion, article matching, stock normalization Security isolation + bursty import workload
platform-export Platform connectors, exports, order import, tracking/invoice push High IO workload and connector-specific retries
vehicle-data Vehicle source sync, fitment rules, mapping/projections Independent data lifecycle and compute
erp-middleware (existing) Odoo sync boundary Already isolated and operational

Constraint: no additional deployables unless the split criteria in this document are met.

Anti-Fragmentation Rules

When to create a new Symfony application

Create a new app only when at least 2 are true:

  1. Distinct trust/security boundary (for example, isolated FTP ingress).
  2. Distinct scaling profile (5x+ higher CPU/IO needs than current host app).
  3. Distinct release cadence requiring independent deploy windows.
  4. Clear data ownership with low synchronous coupling to existing app.

If fewer than 2 are true, keep it as a module in an existing island.

When to create a shared Composer package

Create package only when all are true:

  1. Reused by at least 2 islands.
  2. Stable API expected for 6+ months.
  3. Contains no app-specific ORM entities or business workflows.

If not, keep code local to island.

Shared package cap

Cap internal shared packages at 3:

  1. atraxion/contracts (cross-island event and API DTO contracts only)
  2. atraxion/integration-sdk (HTTP/FTP clients, retry/idempotency utilities)
  3. atraxion/foundation (value objects, ids, money, clock, shared primitives)

Avoid shared Symfony bundles with mutable domain models (Article, Order, Vehicle, etc.).

Integration Model (Suppliers + Platforms)

Use one connector runtime per island, not one service per connector:

  • stock-import: supplier adapters implement SupplierFeedAdapterInterface.
  • platform-export: platform adapters implement PlatformConnectorInterface.
  • Connector config stored in DB (credentials, schedule, format, retry policy, throttling).
  • All connector jobs run through queue workers with:
  • idempotency key per inbound/outbound message
  • exponential backoff
  • dead-letter queue and operator replay tooling

This keeps 50 suppliers and 60 platforms in configuration + plugins, not deployables.

Data Ownership and Contracts

  1. Each island owns its schema and migrations.
  2. No direct cross-island DB reads.
  3. Cross-island communication uses synchronous REST for user-facing reads needing immediate response.
  4. Cross-island communication uses asynchronous events for stock, fitment, platform orders, tracking, and invoice updates.
  5. Apply outbox/inbox pattern for at-least-once delivery without double-processing.

Performance Baseline

Catalog and Stock (Webshop)

Use projection tables optimized for storefront reads:

  • article_availability_projection
  • article id
  • sellable qty
  • best price
  • next delivery date
  • updated timestamp

Updated by stock events from stock-import.

Target:

  • Product/search read endpoints p95 < 200 ms (cached/optimized queries).

Vehicle Fitment (Vehicle Data + Webshop Read Model)

Store precomputed fitment in projection:

  • product_vehicle_mapping keyed by (vehicle_variant_id, article_id)
  • include score, front/rear position, OE marker

Rebuild incrementally on:

  • vehicle source changes
  • product spec changes
  • manual fitment rule changes

Target:

  • Vehicle-to-product lookup p95 < 250 ms.

Wheel Sets (Composite Article Without Native SKU)

Introduce deterministic virtual set key:

  • set_key = hash(wheel_id, tyre_id, tpms_id, mounting_type, axle_config)

Projection table:

  • wheel_set_projection
  • set_key
  • component ids
  • computed sellable qty
  • computed price
  • vehicle scope
  • updated timestamp

Quantity rule (standard set):

  • sellable_qty = floor(min(wheel_qty, tyre_qty) / 4)

For staggered sets, use axle-aware component pairs with explicit front/rear calculation rules.

Target:

  • Set list/detail p95 < 300 ms without runtime brute-force joins.

Export and Import Throughput (Integration Islands)

  1. Use batch processing (chunked reads/writes).
  2. Keep per-connector watermark/checkpoint state for resumable processing.
  3. Use read replicas for heavy export reads when needed.
  4. Separate queues by workload class: high-priority for order imports/status updates, bulk for stock exports/full feed generation.

Runtime and Scalability Defaults

  1. Scale workers horizontally before splitting services.
  2. Use Redis for cache + transient locks.
  3. Use queue-backed async jobs for non-request work (Symfony Messenger).
  4. Keep one relational DB engine family across islands (MySQL/MariaDB) for operational simplicity.
  5. Add a dedicated search engine only when measured query latency/filter complexity exceeds relational targets.

Rollout Strategy

  1. Build all Day 1 islands in one monorepo with strict module boundaries.
  2. Implement contracts + projections first (stock, fitment, sets), then UI/features.
  3. Run dual-write/dual-read validation for stock and set quantities before cutover.
  4. Enforce split/package guardrails in architecture review checklist.

Acceptance Criteria

  1. No more than 5 deployables in Day 1 landscape.
  2. No per-supplier/per-platform deployables created.
  3. Internal shared package count <= 3.
  4. Stock, fitment, and set projections implemented with measurable SLIs.
  5. All cross-island writes are API/event based (no direct DB coupling).