Ecotone
SponsorBlogGithubSupport and ContactCommunity Channel
  • About
  • Installation
  • How to use
    • CQRS PHP
    • Event Handling PHP
    • Aggregates & Sagas
    • Scheduling in PHP
    • Asynchronous PHP
    • Event Sourcing PHP
    • Microservices PHP
    • Resiliency and Error Handling
    • Laravel Demos
    • Symfony Demos
      • Doctrine ORM
  • Tutorial
    • Before we start tutorial
    • Lesson 1: Messaging Concepts
    • Lesson 2: Tactical DDD
    • Lesson 3: Converters
    • Lesson 4: Metadata and Method Invocation
    • Lesson 5: Interceptors
    • Lesson 6: Asynchronous Handling
  • Enterprise
  • Modelling
    • Introduction
    • Message Bus and CQRS
      • CQRS Introduction - Commands
        • Query Handling
        • Event Handling
      • Aggregate Introduction
        • Aggregate Command Handlers
        • Aggregate Query Handlers
        • Aggregate Event Handlers
        • Advanced Aggregate creation
      • Repositories Introduction
      • Business Interface
        • Introduction
        • Business Repository
        • Database Business Interface
          • Converting Parameters
          • Converting Results
      • Saga Introduction
      • Identifier Mapping
    • Extending Messaging (Middlewares)
      • Message Headers
      • Interceptors (Middlewares)
        • Additional Scenarios
      • Intercepting Asynchronous Endpoints
      • Extending Message Buses (Gateways)
    • Event Sourcing
      • Installation
      • Event Sourcing Introduction
        • Working with Event Streams
        • Event Sourcing Aggregates
          • Working with Aggregates
          • Applying Events
          • Different ways to Record Events
        • Working with Metadata
        • Event versioning
        • Event Stream Persistence
          • Event Sourcing Repository
          • Making Stream immune to changes
          • Snapshoting
          • Persistence Strategies
          • Event Serialization and PII Data (GDPR)
      • Projection Introduction
        • Configuration
        • Choosing Event Streams for Projection
        • Executing and Managing
          • Running Projections
          • Projection CLI Actions
          • Access Event Store
        • Projections with State
        • Emitting events
    • Recovering, Tracing and Monitoring
      • Resiliency
        • Retries
        • Error Channel and Dead Letter
          • Dbal Dead Letter
        • Idempotent Consumer (Deduplication)
        • Resilient Sending
        • Outbox Pattern
        • Concurrency Handling
      • Message Handling Isolation
      • Ecotone Pulse (Service Dashboard)
    • Asynchronous Handling and Scheduling
      • Asynchronous Message Handlers
      • Asynchronous Message Bus (Gateways)
      • Delaying Messages
      • Time to Live
      • Message Priority
      • Scheduling
      • Dynamic Message Channels
    • Distributed Bus and Microservices
      • Distributed Bus
        • Distributed Bus with Service Map
          • Configuration
          • Custom Features
          • Non-Ecotone Application integration
          • Testing
        • AMQP Distributed Bus (RabbitMQ)
          • Configuration
        • Distributed Bus Interface
      • Message Consumer
      • Message Publisher
    • Business Workflows
      • The Basics - Stateless Workflows
      • Stateful Workflows - Saga
      • Handling Failures
    • Testing Support
      • Testing Messaging
      • Testing Aggregates and Sagas with Message Flows
      • Testing Event Sourcing Applications
      • Testing Asynchronous Messaging
  • Messaging and Ecotone In Depth
    • Overview
    • Multi-Tenancy Support
      • Getting Started
        • Any Framework Configuration
        • Symfony and Doctrine ORM
        • Laravel
      • Different Scenarios
        • Hooking into Tenant Switch
        • Shared and Multi Database Tenants
        • Accessing Current Tenant in Message Handler
        • Events and Tenant Propagation
        • Multi-Tenant aware Dead Letter
      • Advanced Queuing Strategies
    • Document Store
    • Console Commands
    • Messaging concepts
      • Message
      • Message Channel
      • Message Endpoints/Handlers
        • Internal Message Handler
        • Message Router
        • Splitter
      • Consumer
      • Messaging Gateway
      • Inbound/Outbound Channel Adapter
    • Method Invocation And Conversion
      • Method Invocation
      • Conversion
        • Payload Conversion
        • Headers Conversion
    • Service (Application) Configuration
    • Contributing to Ecotone
      • How Ecotone works under the hood
      • Ecotone Phases
      • Registering new Module Package
      • Demo Integration with SQS
        • Preparation
        • Inbound and Outbound Adapters and Message Channel
        • Message Consumer and Publisher
  • Modules
    • Overview
    • Symfony
      • Symfony Configuration
      • Symfony Database Connection (DBAL Module)
      • Doctrine ORM
      • Symfony Messenger Transport
    • Laravel
      • Laravel Configuration
      • Database Connection (DBAL Module)
      • Eloquent
      • Laravel Queues
      • Laravel Octane
    • Ecotone Lite
      • Logging
      • Database Connection (DBAL Module)
    • JMS Converter
    • OpenTelemetry (Tracing and Metrics)
      • Configuration
    • RabbitMQ Support
    • Kafka Support
      • Configuration
      • Message partitioning
      • Usage
    • DBAL Support
    • Amazon SQS Support
    • Redis Support
  • Other
    • Contact, Workshops and Support
Powered by GitBook
On this page
  • Distribution Bus
  • Command Distribution
  • Sending Distributed Commands
  • Consuming distributed command
  • Event Distribution
  • Publishing Distributed Events
  • Consuming Distributed Events
  • Generic Message Distribution
  • Publishing Distributed Messages

Was this helpful?

Export as PDF
  1. Modelling
  2. Distributed Bus and Microservices
  3. Distributed Bus

Distributed Bus Interface

Distribution Bus

Ecotone does use given Provider abstractions to create topology which can be used to route Messages from one Service to another. When given Provider is enabled as a Publisher, then DistributedBus interface is registered in our Dependency Container. DistributedBus is an interface entrypoint for sending Messages between Services. It does provides way to send Commands, Events and generic Messages (the intention behind the last one will be reveal soon).

interface DistributedBus
{
    // Command distribution
    public function sendCommand() : void;
    public function convertAndSendCommand() : void;

    // Event distribution
    public function publishEvent() : void;
    public function convertAndPublishEvent() : void;
    
    // Generic Message distribution
    public function sendMessage(): void;    
}

For example, when we are sending command, we are stating which Service to we aim to send the Message too. This way Ecotone is aware where given Message should land. When we subscribe to Events

Command Distribution

Command have always only single Message Handler that handle specific Command. One the Service (Application) level this ensured by constraint, as no more than one Command Handler can have same routing key. However if we would simply publish an Command multiple Services could subscribe to it, which would defeat the intention behind Command Message. It's much easier to reason about how the system behaves, when we can rely on Command having single point of handling, and Events having multiple ones.

Therefore to achieve the isolation, when Command is send we are stating given Service Name to which it should land. This ensures that Message will go no where else, and then on the Service level, it's ensured by Ecotone configuration that only single Command Handler will be handling this Message.

Sending Distributed Commands

To send Command Messages we will be using DistributedBus:

interface DistributedBus
{
    public function convertAndSendCommand(
        string $targetServiceName, 
        string $routingKey, 
        object|array $command, 
        array $metadata = []
    ) : void;
    
(...)
  • routingKey - is a routing key under which CommandHandler is registered on targeted Service

public function changeBillingDetails(DistributedBus $distributedBus)
{
    $distributedBus->sendCommand(
        targetServiceName: "billing",
        routingKey: "billing.changeDetails",
        '["personId":"123","billingDetails":"01111"]',
        "application/json"
    );
}

Consuming distributed command

General flow behind consuming Distributed Command Messages is to add Distributed attribute on top of our Command Handler. This way we state the intention that this Command Handler should take a part in Command Distribution.

#[Distributed]
#[CommandHandler("billing.changeDetails")]
public function changeBillingDetails(ChangeBillingDetails $command) : void
{
    // do something with billing details
}

To expose command handler for service distribution mark it with Distributed attribute.

Event Distribution

Events are different from Commands as there may be multiple Services involved into Event subscriptions. Therefore when we publish Event Message we don't state where should it land (Service Name) as from publisher side we are decoupled from that decision. Each Distributed Consumer is stating however which Event it would like to subscribe too. Therefore each Service can connect to subscription, when it becomes interested in particular distributed Event.

Publishing Distributed Events

To send Event Messages we will be using DistributedBus:

interface DistributedBus
{
    public function convertAndPublishEvent(
        string $routingKey, 
        object|array $event, 
        array $metadata
    ) : void;
    
(...)
  • routingKey - is a routing key under which event will be published

public function changeBillingDetails(DistributedBus $eventBus)
{
    $eventBus->publishEvent(
         routingKey: "billing.detailsWereChanged",
         '{"personId":123,"billingDetails":0001}',
         "application/json" 
    );
}

Consuming Distributed Events

General flow behind consuming Distributed Event Messages is to add Distributed attribute on top of our Event Handler. This way we state the intention that this Event Handler should take a part in Event Distribution.

#[Distributed]
#[EventHandler("billing.detailsWereChanged")]
public function registerTicket(BillingDetailsWereChanged $event) : void
{
    // do something with event
}

To start listening for distributed events under specific key, provide Distributed attribute.

Generic Message Distribution

You may distribute generic Message. This allows avoiding registering given Message as Command or Event. This is especially useful, if integration happens not from business requirements (therefore we don't want to include it as Command or Event), but it does happens because of technical requirements.

Ecotone for example use Distributed Messages for communicating between Applications and Ecotone Pulse to inform whatever given message should be replayed or deleted.

Publishing Distributed Messages

To send Generic Messages we will be using DistributedBus:

interface DistributedBus
{
    public function sendMessage(
        string $targetServiceName, 
        string $targetChannelName, 
        string $payload, 
        string $sourceMediaType = MediaType::TEXT_PLAIN, 
        array $metadata = []
    ): void;
    
(...)
  • targetChannelName - is a target channel on the destination side

public function changeBillingDetails(DistributedBus $distributedCommandBus)
{
    $distributedCommandBus->sendCommand(
        targetServiceName: "billing",
        targetChannelName": "billing.changeDetails",
        '["personId":"123","billingDetails":"01111"]',
        "application/json"
    );
}
PreviousConfigurationNextMessage Consumer

Last updated 4 months ago

Was this helpful?

targetServiceName - is a of targeted Service

targetServiceName - is a of targeted Service

Service Name
Service Name