DEV Community

Cover image for Shopware Plugin Architecture: How to Build Update-Safe Plugins
Stefan Pilz
Stefan Pilz

Posted on

Shopware Plugin Architecture: How to Build Update-Safe Plugins

Shopware plugins often fail not because of bugs, but because of architecture decisions that don’t survive updates.

Every major Shopware release exposes the same pattern:

  • plugins that rely on core overrides break
  • fragile hacks stop working
  • maintenance costs explode

This article is a practical guide to building update-safe Shopware plugins that survive:

  • minor updates
  • major versions
  • core refactorings

Why “update-safe” matters in Shopware

Shopware evolves fast:

  • core services are refactored
  • internal logic moves
  • admin & storefront architectures change

If your plugin:

  • overrides core classes
  • depends on internal behavior
  • patches logic at the wrong layer

then updates will break it — not maybe, but eventually.

Update-safe plugins:

  • integrate instead of replace
  • react instead of override
  • extend behavior without owning it

1️⃣ Events instead of core overrides

❌ The classic mistake: overriding core classes

class CustomCartService extends CartService
{
    public function add(...) { ... }
}
Enter fullscreen mode Exit fullscreen mode

This looks simple — and it’s fragile.

Why?

  • core services change signatures
  • internal logic shifts
  • parent behavior becomes unpredictable

✅ The correct approach: react via events

class CartSubscriber implements EventSubscriberInterface
{
    public static function getSubscribedEvents(): array
    {
        return [
            CartChangedEvent::class => 'onCartChanged'
        ];
    }

    public function onCartChanged(CartChangedEvent $event): void
    {
        $cart = $event->getCart();
    }
}
Enter fullscreen mode Exit fullscreen mode

Benefits

  • no dependency on internal implementation
  • forward-compatible
  • predictable execution order

Rule of thumb:

If an event exists, never override the core.


2️⃣ Services over static helpers

❌ Anti-pattern: static helper classes

class PriceHelper
{
    public static function calculate(...) { ... }
}
Enter fullscreen mode Exit fullscreen mode

Problems:

  • no dependency injection
  • hard to test
  • tightly coupled logic
  • difficult to extend

✅ Proper service-based architecture

services:
  MyPlugin\Service\PriceCalculator:
    arguments:
      - '@shopware.cart.calculator'
Enter fullscreen mode Exit fullscreen mode
class PriceCalculator
{
    public function __construct(
        private CartCalculator $calculator
    ) {}

    public function calculate(...) { ... }
}
Enter fullscreen mode Exit fullscreen mode

3️⃣ Subscriber > Decorator (most of the time)

Decorators are powerful — but risky.

Use decorators only when:

  • return values must be modified
  • no suitable event exists
  • logic must be changed synchronously

Subscribers are safer:

  • they do not replace logic
  • they survive refactors better
  • they don’t rely on internal call order

4️⃣ Message Bus for async & heavy logic

❌ Anti-pattern: heavy logic in request lifecycle

Examples:

  • external API calls
  • batch processing
  • AI requests
  • expensive calculations

✅ Use the Message Bus (Symfony Messenger)

class SyncProductMessage
{
    public function __construct(
        public string $productId
    ) {}
}
Enter fullscreen mode Exit fullscreen mode
class SyncProductHandler
{
    public function __invoke(SyncProductMessage $message): void
    {
        // async processing
    }
}
Enter fullscreen mode Exit fullscreen mode

5️⃣ Typical anti-patterns

  • accessing private core services
  • relying on undocumented entity fields
  • overwriting Twig blocks completely
  • business logic in controllers

Always extend, never replace.


6️⃣ Plugin boundaries

A stable plugin:

  • owns its own logic
  • does not own Shopware core behavior
  • integrates via public extension points

Ask yourself:

  • would this survive a major update?
  • does it rely on undocumented behavior?

7️⃣ Practical rules

Do

  • events over overrides
  • services over helpers
  • subscribers over decorators
  • message bus over sync logic

Avoid

  • core inheritance
  • static utilities
  • undocumented internals

Final thoughts

Update-safe Shopware plugins are not about clever hacks.

They are about respecting boundaries.

If your plugin reacts instead of replacing, integrates instead of controlling and stays within public APIs, updates become boring.

And boring is good.


About the author

Stefan Pilz
Shopware Freelancer & Plugin Developer
Website: https://stefanpilz.ltd

Top comments (0)