Business Interface
There may be cases when we will need to call specific
Query Handler
or Command Handler
from inside of your business code.
Introducing Command/Query Bus
in our business code will result in blurring the logic as those are Messaging concepts. Besides Command or Query Buses may be intercepted by middlewares (to verify authorization for example), which may require us to pass some extra details which normally would not be needed.In order to keep our business code clean and to allow for calling Query and Command Handlers from the business code, Ecotone provides
Business Interface
.Let's take as an example creating new Ticket
class TicketService
{
#[CommandHandler("ticket.create")]
public function createTicket(CreateTicketCommand $command) : void
{
// handle create ticket command
}
}
We may define interface, that will call this Command Handler whenever will be executed.
interface TicketApi
{
#[BusinessMethod('ticket.create')]
public function create(CreateTicketCommand $command): void;
}
This way we don't need to use Command Bus and can bypass all Bus related interceptors.
The attribute
#[BusinessMethod]
tells Ecotone that given interface
is meant to be used as entrypoint to Messaging and which Message Handler it should send the Message to.
Ecotone will provide implementation of this interface directly in your Dependency Container.We already used Business Interfaces without being aware,
Command
, Query
and Event
Buses are Business Interfaces.
From lower level API Business Method
is actually a Message Gateway.We may also execute given Aggregate directly using Business Interface.
#[Aggregate]
class Ticket
{
#[Identifier]
private Uuid $ticketId;
private bool $isClosed;
#[CommandHandler("ticket.close")]
public function close(): void
{
$this->isClosed = true;
}
}
Then we define interface:
interface TicketApi
{
#[BusinessMethod('ticket.close')]
public function create(#[Identifier] Uuid $ticketId): void;
}
We may of course pass Command class, if we need to pass data to our Aggregate's Command Handler.
Defining Query Interface works exactly the same as Command Interface and we may also use it with Aggregates.
class TicketService
{
#[QueryHandler("ticket.get_by_id")]
public function getTicket(GetTicketById $query) : array
{
//return ticket
}
}
Then we may call this Query Handler using Interface
interface TicketApi
{
#[BusinessMethod("ticket.get_by_id")]
public function getTicket(GetTicketById $query): array;
}
If we have registered Converter then we let
Ecotone
convert the result of your Query Handler
to specific format. class TicketService
{
#[QueryHandler("ticket.get_by_id")]
public function getTicket(GetTicketById $query) : array
{
//return ticket
}
}
Then we may call this Query Handler using Interface
interface TicketApi
{
#[BusinessMethod("ticket.get_by_id")]
public function getTicket(GetTicketById $query): TicketDTO;
}
Ecotone will use defined Converter to conver
array
to TicketDTO
.Such conversion are useful in order to work with objects and to avoid writing transformation code in our business code.
Special type of
Business Interface
is Repository
.
If you want to fetch or store Aggregate register under Ecotone's Repository you may use #[Repository]
attribute. interface OrderRepository
{
#[Repository]
public function getOrder(string $twitId): Order;
#[Repository]
public function findOrder(string $twitId): ?Order;
#[Repository]
public function save(Twitter $twitter): void;
}
Ecotone will read type hint to understand, which Aggregate you would like to fetch or save.
Implementation will be delivered by Framework. All you need to do is to define the interface and it will available in your Dependency Container
interface OrderRepository
{
#[Repository]
public function getOrder(string $twitId): Order;
#[Repository]
public function findOrder(string $twitId): ?Order;
#[Repository]
#[RelatedAggregate(Order::class)]
public function save(string $aggregateId, int $currentVersion, array $events): void;
}
The difference is in
save
method, you need to provide aggregate id, current aggregate's version and array of events
you would like to store.Last modified 1mo ago