Message Based — Business Oriented Interfaces
Messaging combined with Business Interfaces, can provide us with smooth development experience, testable code and a lot of extra features.
Message based-Business Oriented Interfaces
Laravel Fascades provides great way to quickly access given functionality. This as a result make the development experience smooth, as we simplify stating what we want to achieve and we get it. However this comes with the down side, as Fascades are based on static methods and bounds us directly to specific implementation. This as a result make it cumbersome to switch the implementation and to actually test it.
On the other side we have development based on abstractions. Instead of binding and hard coding given solution, we use Interfaces in order to decouple from the implementation. This way we get the ability to provide stubs and mocks, making the code easy to test.
The down side of this approach however, is that development experience is not so smooth anymore. Now we need to start thinking about delegations, adapters and wiring things together.
So the trick is to keep smooth and fast development experience, yet to produce code that is maintainable and testable at the same time.
The guarantee of the above is Messaging, as it allows for ease of binding things, yet comes with decoupled nature, so it’s easy to add, replace and modify components. Therefore let’s check how we can make this happen with Ecotone, using Business Interfaces which are based on Messaging.
Activating Messaging
Suppose we have Caching Service. This Service does not follow any particular interface, it’s just stand alone Class we’ve introduced.
Caching Service does require CachedItem, which contains of key, value.
This Service can be placed anywhere in the code e.g. Service layer, Infrastructure Layer, some Tooling Namespace, whatever fits our application.
Now we want to activate this Service to be accessible via Messaging. And to do it we will use PHP Attribute - ServiceActivator.
We’ve added Attributes without changing single line of application level code. However that it’s enough to enable Ecotone’s Messaging and to open us for a lot of possibilities, including usage of Business Interface.
With Ecotone, Messaging can be used within existing code base with ease. Ecotone will do all the bindings, and necessary configuration, so we can focus on the business aspects of the application. Thanks to that it does not really matter if we work in legacy or green field project, to start using Messaging it’s enough to mark given method with Attribute.
Business Interface
Business Interface is no more than simple Interface defined in the code, marked with BusinessMethod Attribute. It works as a Gateway to execute given Messaging Endpoint (Service Activator).
We don’t need to implement this Interface anywhere in the code, Ecotone will deliver implementation for this, which will be automatically registered in Depedency Container. This means we can already inject and start using this Interface, even so we have not defined implementation.
When we will execute this method, under the hood Ecotone will construct and send this Message. This will execute activated Service, in our case Caching Service.
As a result we get simplicity of Fascade, as the only thing we need to create is an interface and given piece of functionality will be executed. And still we have the power of switching the implementation for testing purposes.
Business Method is efficient way to expose any Messaging functionality in form of simple Interface.
The best part is, if you’ve legacy code, you can expose it functionality without changing single line of code, by combing Service Activator with Business Method.
Example implementation is available in quick-start examples repository.
Command Business Methods
If we are using Ecotone’s Command Handlers, we may expose them via Business Interface too. This allows us to avoid using Command Bus in the code base, and to bypass Middlewares related to the Bus.
As an example, let’s take Create Ticket Command Handler.
Normally we would use Command Bus to execute this, yet we can define our own Business Interface that will execute this Command Handler directly.
This works exactly the same as Service Activator, yet the difference is in visibility. Service Activator is private and can be executed only by Business Interface, where Command Handler can be executed by Business Interface and the Command Bus.
By using Business Methods we can create Domain specific Interfaces and bind them directly to Message Handlers of our choice. Thanks to that, a lot of boilerplate code that we would normally write and maintain is no longer needed.
Query Business Methods
Business Interface can also be used for Queries with additional support for conversions. Let’s follow up on Ticket example and introduce TicketQueryHandler:
And then we can define it on our Business Interface:
Business Method will return whatever Query Handler returns. So in our example it will be most likely an associative array.
However from the side of Business Interface, we may want to work with higher level code than array. For this we can change the return type in the Business Method, and Ecotone will do conversion before it’s returned:
To tell Ecotone how to do the conversion, we can use inbuilt mechanism using Converters or use our own registered Converter. To use inbuilt mechanism, all we would have to do is to create an conversion method:
Business Interfaces can also be used for querying and together with Conversion support, we can create nice decoupled Interfaces. By using Ecotone’s conversion, we get ability to build Anti Corruption Layers between Modules with ease.
Passing Message Headers
With Command/Query Bus we can pass additional Metadata and with Business Interface it’s the same. Metadata can be passed in form of Message Headers. This is a way to provide additional information along side with Command or Query class instance.
After defining given Message Header we can access it within Message Handler:
Message Headers are great way to pass additional metadata in order to avoid putting passing unrelated data to the Command or Query itself.
Conversion also work on the Message Handler level, so if we would type hint executorId to be class like Uuid or ExecutorId, Ecotone will convert it from string to given type.
Routing and Different Strategies
Let’s imagine we’ve introduced FileSystem Cache along side to InMemory one. At this moment InMemory Cache is executed directly from Business Method, so if we want to switch dynamically we need a router in between.
Ecotone provides an Router implementation, which is responsible for for routing given Message to specific Message Endpoint (Service Activator).
Router is an simple Service within our Application, just like Service Activators. There is no custom framework logic involved into the process, so the routing logic can be tested easily.
In our scenario, we want to decide where given item should be cached. By returning the routing key, we are stating where given message should be routed.
When we’ve Router defined, we can define Service Activators for different Cache Types:
Routing keys under which each Service Activator is registered are corresponding to the routing keys returned by the Router.
Now we can enrich our Business Interface to provide the cache.type Header:
In general there are no limits on how we want to define our Routing logic. We could base it on the CachedItem, we could fetch the information from outside, do it based on item size etc. As Router is simple a Service within our Application, we have full control over the process.
Example implementation is available in quick-start examples repository.
Asynchronous Processing and Batching
There may be a cases where we would like handle batch of operations.
In those situations we may want to push processing to the background, to avoid waiting for each operation to complete in order to finish the script.
With Ecotone’s Messaging asynchronous processing becomes trivial, as all we need to do it, is to state that using Attribute.
We’ve defined this Service Activator to work asynchronously. Before this method will be executed, Message will go through Message Channel named “async”. So what is left is to define what kind of Message Channel it’s.
ServiceContext is attribute indiciating that given method returns Ecotone’s configuration. In this example we are using Database based Message Channel. There are multiple implementations we can choose from.
We don’t need to change Business Interface and invocations, they stay exactly the same like in synchronous version. So all we did, was to add attribute to define that given Service Activator should work asynchronously.
Performing given actions asynchronously is really powerful. Instead of doing everything all together in one go (which is time consuming and prune to errors), we handle given method asynchronously. This way each invocation is handled in full isolation and Message Consumption can be scaled.
Example implementation is available in quick-start examples repository.
Aggregate Business Methods
Aggregate methods can also be exposed via Business Methods. This is possible when using Ecotone’s Aggregate support.
To execute given business method on specific Aggregate instance, we need to state what Identifier should be used for fetching the Ticket Aggregate.
This way Ecotone will load Ticket Aggregate having defined $ticketId, then execute close method and save the Aggregate. Loading and saving will happen with registered Repositories.
In this example we are executing Command Handler without any Command Class. This is possible thanks to Ecotone’s Message Routing. In cases where Commands are redundant we can simply not use them at all.
Example implementation is available in quick-start examples repository.
Summary
Business Oriented Interface works as API for given set of methods.
It’s really easy to bind any functionality to Business Interface, no matter if you work in legacy code or a green field project.
On top of that we get huge amount of features including, Result Conversion, Asynchronous Processing and Invoking Aggregates.
This makes the Business Interfaces a powerful solution for quick and smooth development which keeps the code maintainable and testable at the same time.