Skip to content

Commit

Permalink
Update strategy-resolver.md
Browse files Browse the repository at this point in the history
  • Loading branch information
andriitserkovnyi committed Feb 28, 2025
1 parent 940af4c commit 4c643e6
Showing 1 changed file with 28 additions and 174 deletions.
202 changes: 28 additions & 174 deletions docs/dg/dev/miscellaneous-guides/strategy-resolver.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
title: Strategy Resolvers
title: Strategy resolvers
description: This document
last_updated: Feb 6, 2025
template: howto-guide-template
Expand All @@ -8,25 +8,22 @@ redirect_from:

---

Spryker introduces context-based dependency resolution using a strategy resolver to manage complex workflows spanning multiple modules. This enhancement allows defining multiple dependencies and dynamically selecting the appropriate one at runtime based on business logic.
Context-based dependency resolution using a strategy resolver is used to manage complex workflows spanning multiple modules. This lets you define multiple dependencies and dynamically select the appropriate one at runtime based on business logic.

Strategy resolvers are introduced because some workflows require multiple plugin-stack variations that need to be switched in sync.

The approach is backward compatible. It's applied only to selected plugin-stacks that require this enhancement, and only customizations may need updates.

Use strategy resolvers in the following cases:
- To handle multiple plugin-stack configurations dynamically
- For workflows that spans multiple modules and requires synchronized behavior
- To allow context-specific behavior while keeping plugin-stack resolution flexible

Use the strategy resolver in the following cases:
- You need to handle multiple plugin-stack configurations dynamically.
- A workflow spans multiple modules and requires synchronized behavior.
- You want to allow context-specific behavior while keeping plugin-stack resolution flexible.
Don't strategy resolvers in the following cases:
- A single plugin-stack is sufficient for your module, so consider using regular strategy plugin stack approach
- Context-based, wide-spread synchronization is not needed

Don't use it in the following cases:
- A single plugin-stack is sufficient for your module (considering using regular strategy plugin stack approach instead).
- You do not need context-based, wide-spread synchronization.

## How It Works (Technical Details & Code Examples)

### Defining Contexts
## Context definition

Each module that provides a context must define it as an interface constant with a clear and verbose description of its purpose.

Expand All @@ -52,12 +49,18 @@ interface CheckoutExtensionContextsInterface
public const CONTEXT_CHECKOUT = 'checkout';
```

### Configuring Strategy Resolver in the Factory
The following section defines how to configure a fallback in case context is missing.

## Strategy resolver configuration in the factory

The factory is responsible for the following:
* Creating the strategy resolver
* Mapping all supported contexts to plugin stacks
* Choosing fallbacks

The factory is responsible for creating the strategy resolver and mapping all supported contexts to plug-in stacks and choose fallbacks if needed.
It is important to well define the generic type of the strategy resolver to ensure type safety.
To ensure type safety, the generic type of the strategy resolver must be well-defined.

The below example defines plugin stacks for `Checkout` and `Order Amendment` contexts, while keeps `Checkout` context as fallback for backward compatibility reasons.
The following example defines plugin stacks for `Checkout` and `Order Amendment` contexts while keeping `Checkout` context as fallback for backward compatibility.

```php
/**
Expand Down Expand Up @@ -93,9 +96,9 @@ Instead of injecting a single plugin-stack, inject the strategy resolver into th
}
```

### Using Context-Based Resolution in the Model
## Context-based resolution in models

When the model executes logic, it must explicitly specify the context when requesting a plug-in stack.
When a model executes logic, it must explicitly specify the context when requesting a plugin stack.

```php
class CheckoutWorkflow implements CheckoutWorkflowInterface
Expand Down Expand Up @@ -133,167 +136,18 @@ class CheckoutWorkflow implements CheckoutWorkflowInterface

```

### Enabling Lazy Plugin-Stack Resolution

When multiple plugin-stacks are present with huge (10+) amount of plugins, performance may be an issue. For this reason,
lazy loading is also introduced to the container system.
The strategy resolver solves lazy loading in the background, ensuring that the correct plugin-stack is resolved only when requested.

Note: The lazy loading can be used on demand even without strategy resolver, however fetching needs to be triggered on-demand.

```php
$this->getProvidedDependency(CheckoutDependencyProvider::CHECKOUT_PRE_CONDITIONS, static::LOADING_LAZY);
```

### Debugging & Logging

Everything remains typed, ensuring full compatibility with tools like Xdebug.
The strategy resolver uses a generic template directive, allowing developers to inspect available plug-in stacks.

### Frequently Asked Questions (FAQ)

Q: Should I use this for all plugin-stacks?
A: No, strategy design pattern comes with some complexity, so it is recommended to use only when business logic requires it.

Q: Does this affect performance?
A: Only if multiple plugin-stacks are resolved simultaneously. Use lazy loading to mitigate this.

Q: What happens if a context is missing?
A: By default, it fails. However, you can define a fallback mechanism.







# Context-Based Dependency Resolution in Spryker



## When to Use It


Do not use it if:
- A single plugin-stack is sufficient (consider using the regular strategy plugin stack approach instead).
- Context-based, wide-spread synchronization is not needed.

## How It Works (Technical Details & Code Examples)

### Defining Contexts
Each module providing a context must define it as an interface constant with a clear and verbose description.

```php
<?php

namespace Spryker\Shared\CheckoutExtension;

interface CheckoutExtensionContextsInterface
{
/**
* Specification:
* - Defines the Checkout Context for initiating a new order.
* - Differentiates new order processing from other order-related operations.
* - Enables context-specific logic execution.
*
* @api
*
* @var string
*/
public const CONTEXT_CHECKOUT = 'checkout';
```

### Configuring the Strategy Resolver in the Factory
The factory creates the strategy resolver, mapping contexts to plugin stacks while ensuring type safety.

```php
/**
* @return \Spryker\Shared\Kernel\StrategyResolverInterface<list<\Spryker\Zed\CheckoutExtension\Dependency\Plugin\CheckoutPreConditionPluginInterface>>
*/
public function createCheckoutPreConditionPluginStrategyResolver(): StrategyResolverInterface
{
return new StrategyResolver(
[
CheckoutExtensionContextsInterface::CONTEXT_CHECKOUT => $this->getProvidedDependency(CheckoutDependencyProvider::CHECKOUT_PRE_CONDITIONS, static::LOADING_LAZY),
SalesOrderAmendmentExtensionContextsInterface::CONTEXT_ORDER_AMENDMENT => $this->getProvidedDependency(CheckoutDependencyProvider::CHECKOUT_PRE_CONDITIONS_FOR_ORDER_AMENDMENT, static::LOADING_LAZY),
],
CheckoutExtensionContextsInterface::CONTEXT_CHECKOUT, // fallback context
);
}
```

Instead of injecting a single plugin-stack, inject the strategy resolver into the model:

```php
/**
* @return \Spryker\Zed\Checkout\Business\Workflow\CheckoutWorkflowInterface
*/
public function createCheckoutWorkflow()
{
return new CheckoutWorkflow(
$this->getOmsFacade(),
$this->createCheckoutPreConditionPluginStrategyResolver(),
$this->createCheckoutSaveOrderPluginStrategyResolver(),
$this->createCheckoutPostSavePluginStrategyResolver(),
$this->createCheckoutPreSavePluginStrategyResolver(),
);
}
```

### Using Context-Based Resolution in the Model
The model must explicitly specify the context when requesting a plugin stack.
### Lazy plugin-stack resolution and performance

```php
class CheckoutWorkflow implements CheckoutWorkflowInterface
{
/**
* @var \Spryker\Shared\Kernel\StrategyResolverInterface<list<\Spryker\Zed\CheckoutExtension\Dependency\Plugin\CheckoutPreConditionPluginInterface>>
*/
protected $preConditionPluginStrategyResolver;

public function __construct( ... StrategyResolverInterface $preConditionPluginStrategyResolver ...) {
$this->preConditionPluginStrategyResolver = $preConditionPluginStrategyResolver;
}

protected function checkPreConditions(QuoteTransfer $quoteTransfer, CheckoutResponseTransfer $checkoutResponse)
{
$isPassed = true;

$quoteProcessFlowName = $quoteTransfer->getQuoteProcessFlow()?->getNameOrFail(); // Example resolution
$preConditionPlugins = $this->preConditionPluginStrategyResolver->get($quoteProcessFlowName);
When there're multiple plugin-stacks with a big number (10+) of plugins, performance may be an issue. To solve this, lazy loading is used in the container system.

foreach ($preConditionPlugins as $preConditionPlugin) {
$isPassed &= $preConditionPlugin->checkCondition($quoteTransfer, $checkoutResponse);
}

return (bool)$isPassed;
}
}
```
The strategy resolver solves lazy loading in the background, ensuring that the correct plugin-stack is resolved only when requested.

### Enabling Lazy Plugin-Stack Resolution
When multiple plugin-stacks contain a large number of plugins (10+), performance may become an issue. Lazy loading addresses this.
The lazy loading can be used on demand even without strategy resolver, however fetching needs to be triggered on demand.

```php
$this->getProvidedDependency(CheckoutDependencyProvider::CHECKOUT_PRE_CONDITIONS, static::LOADING_LAZY);
```

### Debugging & Logging
- Full compatibility with tools like Xdebug.
- Generic template directive ensures inspection of available plugin stacks.

## Frequently Asked Questions (FAQ)

### What kind of semantic versioning lock needs to be applied on the changes of the Factory when I define a new context?
Minor (as default). When a new context is introduced in a Project, it needs to be wired in the [Factory definition](#Configuring the Strategy Resolver in the Factory); any project change in a Spryker Factory requires a minor semantic versoning lock on that package.

### Should I use this for all plugin-stacks?
No, the strategy design pattern introduces complexity. Use it only when business logic requires it.

### Does this affect performance?
Only when multiple plugin-stacks are resolved simultaneously. Use lazy loading to mitigate this.

### What happens if a context is missing?
By default, it fails. However, a fallback mechanism can be defined.
## Debugging and logging

o
The strategy resolver uses a generic template directive, allowing developers to inspect available plug-in stacks. Everything remains typed, ensuring full compatibility with tools like Xdebug.

0 comments on commit 4c643e6

Please sign in to comment.