.claude/skills/ecotone-identifier-mapping/SKILL.md
Implements identifier mapping for Ecotone aggregates and sagas: native ID resolution, aggregate.id metadata, #[TargetIdentifier], identifierMapping expressions, and #[IdentifierMethod]. Use when wiring commands/events to aggregates or sagas by identifier, resolving aggregate IDs from messages, or mapping event properties to saga identifiers.
npx skillsauth add ecotoneframework/ecotone-dev ecotone-identifier-mappingInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
When a command or event targets an existing aggregate or saga, Ecotone must resolve which instance to load. The identifier is resolved in this priority order:
aggregate.id metadata — override via message headers (highest priority)#[Identifier] property name#[TargetIdentifier] — explicit mapping on command/event class propertyidentifierMapping — expression-based mapping on handler attributeidentifierMetadataMapping — header-based mapping on handler attributeUse #[Identifier] on the identity property of an aggregate or saga:
use Ecotone\Modelling\Attribute\Aggregate;
use Ecotone\Modelling\Attribute\Identifier;
#[Aggregate]
class Order
{
#[Identifier]
private string $orderId;
}
When the command/event property name matches the aggregate's #[Identifier] property name, mapping is automatic:
class CancelOrder
{
public function __construct(public readonly string $orderId) {}
}
#[Aggregate]
class Order
{
#[Identifier]
private string $orderId;
#[CommandHandler]
public function cancel(CancelOrder $command): void
{
// $orderId resolved automatically from $command->orderId
}
}
aggregate.id Metadata OverridePass the identifier directly via message metadata. Overrides all other mapping strategies:
$commandBus->sendWithRouting('order.cancel', metadata: ['aggregate.id' => $orderId]);
#[TargetIdentifier] on Commands/EventsWhen the command/event property name differs from the aggregate/saga identifier:
use Ecotone\Modelling\Attribute\TargetIdentifier;
class OrderStarted
{
public function __construct(
#[TargetIdentifier('orderId')] public string $id
) {}
}
identifierMapping on Handler AttributesUse expressions to map identifiers from the payload or headers:
#[EventHandler(identifierMapping: ['orderId' => 'payload.id'])]
public function onExisting(OrderStarted $event): void
{
$this->status = $event->status;
}
identifierMetadataMapping on Handler AttributesMaps aggregate/saga identifiers to specific metadata header names:
#[EventHandler(identifierMetadataMapping: ['orderId' => 'paymentId'])]
public function finishOrder(PaymentWasDoneEvent $event): void
{
$this->status = 'done';
}
#[Identifier] names are resolved automatically (native mapping)aggregate.id metadata overrides all other mapping — use it for routing-key-based commands without message classes#[TargetIdentifier('identifierName')] maps a differently-named property to the aggregate/saga identifieridentifierMapping supports expressions: 'payload.propertyName' and "headers['headerName']"identifierMetadataMapping maps identifiers to header names directly (simpler than identifierMapping for headers)identifierMetadataMapping and identifierMapping on the same handler#[IdentifierMethod('identifierName')] when the identifier value comes from a method rather than a property#[Identifier], #[TargetIdentifier], #[IdentifierMethod], identifierMapping, and identifierMetadataMapping. Load when you need exact constructor parameters, types, or expression syntax.aggregate.id override (including multiple identifiers), #[TargetIdentifier] full saga flow, identifierMapping from payload and headers, identifierMetadataMapping, and #[IdentifierMethod]. Load when you need full, copy-paste-ready class definitions.aggregate.id override, #[TargetIdentifier] with sagas, identifierMapping from payload, and identifierMapping from headers. Load when writing tests for identifier resolution.development
Implements workflows in Ecotone: Sagas (stateful process managers), stateless workflows with InternalHandler and outputChannelName chaining, and Orchestrators (Enterprise) with routing slip pattern. Use when building Sagas, process managers, multi-step workflows, long-running processes, handler chaining, or Orchestrators.
development
Writes and debugs tests for Ecotone using EcotoneLite::bootstrapFlowTesting, aggregate testing, async-tested-synchronously patterns, projections, and common failure diagnosis. Use when writing tests, debugging test failures, adding test coverage, or implementing any new feature that needs tests. Should be co-triggered whenever a new handler, aggregate, saga, projection, or interceptor is being implemented.
testing
Sets up Ecotone in a Symfony project: composer installation, bundle registration, YAML configuration, Doctrine ORM integration, SymfonyConnectionReference for DBAL, Symfony Messenger channels, async consumer commands, and ServiceContext. Use when installing Ecotone in Symfony, configuring Symfony-specific connections, or setting up Symfony async consumers.
development
Implements message resiliency in Ecotone: RetryTemplateBuilder for retry strategies, error channels, ErrorHandlerConfiguration, DBAL dead letter queues, outbox pattern for guaranteed delivery, and FinalFailureStrategy for permanent failures. Use when handling failed messages, configuring retries, setting up dead letter queues, implementing outbox pattern, or managing error channels.