Dit Petje – Multi-brand productontdekkingsplatform

Eén Symfony-codebase, drie merken: Dit Petje, Dit Horloge en Dit Parfum

Houd me op de hoogte

Eén e-mail bij belangrijke updates. Geen tracking, eenvoudig uit te schrijven.

Zusterconcepten: dithorloge.nl, ditparfum.nl

Gegevens worden uitsluitend gebruikt voor de bevestigingsmail en updates. Eén klik om uit te schrijven.

Dit Petje

Over het platform

Dit Petje is een multi-channel productontdekkings­platform waarmee bezoekers per categorie producten doorzoeken, vergelijken en doorklikken naar aanbieders. Onder de motorkap draait MFP (Multi Feed Processing): één Symfony-codebase die drie consumentenmerken bedient — ditpetje.nl (petten en hoeden), dithorloge.nl (horloges) en ditparfum.nl (parfum) — en architectonisch is opgezet om N kanalen aan te kunnen via dynamic discovery (geen hardcoded limiet).

Samenwerking tussen merk en bouwer

Concept, marketing en innovatieplannen komen vanuit ten Bruggencate Marketing; ontwikkeling, doorontwikkeling en beheer nemen wij voor onze rekening. De rolverdeling zorgt ervoor dat marktinzicht en techniek hand-in-hand evolueren zonder dat één discipline de ander blokkeert.

Architectuur in één zin

Feed-bronnen → MFP Dashboard → kanaal-sites → gekoppelde socials. Producten worden centraal ingelezen, per kanaal gefilterd en gescoord, en pas getoond als ze door de display-gates komen.

Vijf feed-bronnen, één pijplijn

  • Awin (6 uur), Bol.com (12 uur), Amazon (24 uur), TradeTracker (12 uur) en Zalando (12 uur) — elke bron heeft zijn eigen schedule, validators en feed-status (active / blocked / paused).
  • Een global-sync orchestrator (feed:sync:global) downloadt elke bron één keer en routeert producten naar alle relevante kanalen — voorkomt dubbele downloads bij groei naar meer kanalen.
  • Per-bron parsing via een gedeelde FeedServiceTrait met centrale relevantie-detectie (Nederlandse compound-keywords als sportpet, winterpet worden expliciet ondersteund) — geen knip-en-plak per bron.
  • Geblokkeerde feeds markeren bestaande producten automatisch als niet-beschikbaar; gepauzeerde feeds skippen sync zonder data te verliezen.

Strikte kanaalscheiding

  • EAN-lookups inclusief channelId — voorkomt cross-channel matching wanneer hetzelfde product op meerdere kanalen verschijnt.
  • Per-channel config in channel.{id}.yaml — eigen prijs-floor/ceiling, keyword-allowlists, display-thresholds en validatieregels.
  • Per-channel Matomo site-IDs — analytics blijft schoon en aggregatie per merk klopt.
  • Display-gates per kanaalmin_display_score en require_image_for_display bepalen of een product zichtbaar wordt; admin-views zien alles, frontend alleen het kwaliteitsfilter.
  • Composite scoring combineert prijs, beschikbaarheid, beeld en feed-betrouwbaarheid tot één rankingscore per product per kanaal.

Drie merken, drie design-systemen

Elk merk heeft zijn eigen brand-design-system (kleur, typografie, tone) — geserveerd vanuit dezelfde Symfony-applicatie via een ChannelService die templates, routes en configuratie scopt. Brand-namen, domein-namen en channel-defaults worden nooit hardcoded; alles loopt via een channel-context. Dat maakt het toevoegen van een vierde merk een config-actie, geen rewrite.

Redactie en SEO/GEO/WCAG

  • Redactionele guides per merk met kanaalspecifieke auteur-defaults — beheerd in een eigen admin-omgeving.
  • Product structured data met offers, price, priceCurrency en availability; producten zonder afbeelding krijgen géén schema (Google-vereiste).
  • CLS-bewuste typografie — Inter met display=optional, icoonfont met expliciete dimensies tegen layout-shift.
  • Mobile-first ontwerp- en build-discipline op consumer-pages; SEO, GEO en WCAG worden tijdens ontwikkeling toegepast, niet achteraf.
  • Pinterest-export-feed en social-koppelingen per kanaal — content stroomt vanaf de site naar de connected socials.

Operationele weerbaarheid

  • Auto-maintenance — kanalen met nul beschikbare producten gaan automatisch in 503-modus (60s gecached); voorkomt lege productpagina's tijdens import-gaps.
  • /health endpoint dat altijd 200 JSON teruggeeft, gebruikt voor Docker-healthchecks (skipt maintenance- en rate-limit-subscribers).
  • Redis voor sessies én voor de live activity-feed in het dashboard — graceful degradation als Redis tijdelijk wegvalt.
  • Async messenger (twee transports: feed_sync + enrichment) houdt feed-imports en AI-verrijking buiten de request-cycle.
  • HTTP-smoke-tests per kanaal-domein als verplicht onderdeel van de release-flow — PHPUnit alleen dekt geen Traefik/Apache-config-issues.

Techniek en kwaliteit

  • Symfony 7.4 op PHP 8.3, Doctrine ORM, Twig 3.23 en Bootstrap 4.6 — productieversie v3.15.0.
  • 1693 tests / 4159 assertions via PHPUnit 11; PHPStan level 6 met 0-error-baseline over 439 bestanden; PHP-CS-Fixer (PER-CS2) en phplint per release.
  • Bitbucket Pipelines draait phplint, CS-Fixer, PHPStan en PHPUnit op elke master-merge en deployt vervolgens via SSH.
  • Docker + Traefik dev- en productiestack; MySQL met custom config; OPcache afgesteld op de codebase (14k+ files, max_accelerated_files=20000) tegen silent recompilation.

Resultaat

Drie merken, één onderhoudbare codebase, een gestage release-cadans (huidige major al op v3.x) en directe lijnen tussen marketingstrategie en technische uitvoering. Dit Petje fungeert ook als proeftuin voor patronen die terugkeren in opdrachtwerk — zoals de multi-brand-aanpak bij Stenenwinkeltje en in de Shopware Plugin Suite.