Software architecture is all about simplicity. In this post, I’ll show you the process of building the architecture of Mokai, an open source Java Messaging Gateway capable of receiving and routing messages through a variety of mechanisms and protocols including HTTP, Mail, SMPP and JMS (Java Message Service), among others.
The black box
Applications send and receive messages to and from external services such as an SMSC, a SMTP server, a JMS queue, etc. In the picture, we can identify the first three challenges of the gateway:
- Receiving messages from applications using any protocol.
- Sending messages to external services using any protocol.
- Routing messages internally.
Let’s start with the last point. How do we route messages internally? Well, with so many protocols for receiving and sending messages, we’ll need a Message class we can manipulate inside Mokai. It will contain some basic information, a body and a map of properties. This way, we can encapsulate any type of information we want inside the message (in the body or properties).
When Mokai receives a message, it will have to be normalized (i.e. encapsulated in a Message object) so it can be routed through the gateway. Once we are ready to send the message to an external service, it will have to be de-normalized (i.e. converted to a protocol that the external service can understand). Easy, right?
Receivers and Processors
The other two challenges we have described above involve extensibility. Anyone should be able to create new protocols for receiving messages or routing them outside the gateway. When creating extension points for your application, make them as simple as you can. In this case, we will introduce two interfaces: Receiver and Processor. Users will have to implement these interfaces to introduce new protocols for receiving or sending messages.
The Processor interface is really straightforward. Nothing strange in there. It just exposes two methods; one for checking if the processor supports a Message and other to process it (de-normalizing it to the desire protocol).
The Receiver interface is even simpler; it exposes no methods, but it uses an instance of a MessageProducer to route the received messages inside Mokai. The MessageProducer will be injected automatically to the Receiver at runtime.
When Mokai receives a message from an application, it will have to decide which processor will handle it. To achieve this, each processor will have a collection of acceptors attached. Acceptors are an extension point of the application, so we introduce the Acceptor interface.
The Acceptor interface exposes only one method that returns true if the acceptor accepts the message or false if it rejects it. When a message is received, a router will query each group of acceptors, based on a priority, to select the processor that will process the message.
So far, our architecture looks like this:
We have introduced a new element in the diagram: a router; it’s task is to query all acceptors and processors to see who will handle the message.
Executing actions on messages
It is possible that we might want to execute actions on a message such as validate it, transform it or re-route it to a different location. For this cases, we introduce a new extension point, the Action interface.
The Action interface exposes only one method to execute the action on the message. There are three places where we can execute actions on a message:
- After a message is received (post-receiving actions).
- Before the message is processed (pre-processing actions).
- After the message is processed (post-processing actions).
Now, with the introduction of actions, the complete architecture of Mokai looks like this:
We have encapsulated the receiver in a Receiver Service and the processor in a Processor Service. A Processor Service also has a collection of post-receiving actions not shown in the diagram (processors can act as receivers too). We have also introduced two queues to store the messages until they are processed.
The class diagram
Now that we’ve seen the architecture of Mokai, let’s see the final class diagram that models the architecture.
In this diagram we can see all the elements described in the architecture and their relationships.
As you can see, Mokai’s architecture is simple, elegant and powerful. It provides all the elements we need to extend the platform to our will, keeping extension points simple and manageable.
In the first release of Mokai, there are also additional services such as message persistence, a plugin mechanism, an administration console and a configuration module, among others. You can check the project homepage for more information.