Internet transactions, business-to-business systems, peer-to-peer processes, and real-time workflows are too dynamic and too complex to be modeled by traditional sequential-processing methods. Therefore, the need for more sophisticated asynchronous processing techniques is quickly becoming apparent. To address these unpredictable environments, the current trend in systems architecture is service-oriented design and event-driven programming.
A service-oriented architecture (SOA) presents a dynamic runtime environment, where loose couplings between service providers and/or service consumers enable powerful and flexible component interactions. Building a communication model to exploit this power and flexibility is a high priority for competitive software development. An event-driven communication model is able to respond better to real-time changes and stimuli than conventional request/reply mechanisms.
Service-oriented and event-driven architectures are natural fits for distributed systems since they share many of the same characteristics, such as modularity, loose-couplings, and adaptability.
In this article, I discuss the details of designing an effective event-driven and service-oriented platform using Mule, a lightweight event-messaging framework designed around an enterprise service bus (ESB) pattern. Components and applications can use Mule to communicate through a common messaging framework implemented transparently using Java Message Service (JMS) or another messaging technology.
Overview of service-oriented architecture
The term "service-oriented" has evolved to define an architecture where a service is a software component that embodies a core piece of an enterprise's business logic and features the following characteristics:
- Loosely coupled:Services are not fundamentally bound with other components
- Protocol-independent:Multiple protocols can transparently access a given service
- Location-agnostic:A given service typically performs a composite form of business logic and returns the result in a single call
- Coarse-grained:Services can be accessed in the same manner no matter their location
- Maintains no user state
Services typically focus exclusively on solving business-domain problems.
Generally, service clients rely on configuration data, registries, and software factories to determine the location, protocol, and public interface for each service.
Applications are typically described by what they do, not necessarily by what they are or what they contain. For this reason, it's much more straightforward to describe an application publicly using verbs (services) as opposed to nouns (objects). Since objects define a thing and not an action, an impedance mismatch can occur when attempting to encapsulate what a component does as opposed to what a component is. In SOA, an application is described naturally, since each logical business operation of the application that can be verbalized is a likely candidate for a service. Therefore, SOA solves the impedance mismatch by allowing applications and components to access the functionality of services based on what the services do, i.e., what actions they perform. In turn, application developers can more easily match their needs with the appropriate services, since the interfaces for the services describe more completely the problems they solve.
Overview of event-driven architecture
An event-driven architecture (EDA) defines a methodology for designing and implementing applications and systems in which events transmit between loosely coupled software components and services. An event-driven system is typically comprised of event consumers and event producers. Event consumers subscribe to an intermediary event manager, and event producers publish to this manager. When the event manager receives an event from a producer, the manager forwards the event to the consumer. If the consumer is unavailable, the manager can store the event and try to forward it later. This method of event transmission is referred to in message-based systems as store and forward.
Building applications and systems around an event-driven architecture allows these applications and systems to be constructed in a manner that facilitates more responsiveness, since event-driven systems are, by design, more normalized to unpredictable and asynchronous environments.
Benefits of event-driven design and development
Event-driven design and development provide the following benefits:
- Allows easier development and maintenance of large-scale, distributed applications and services involving unpredictable and/or asynchronous occurrences
- Allows new and existing applications and services to be assembled, reassembled, and reconfigured easily and inexpensively
- Promotes component and service reuse, therefore enabling a more agile and bug-free development environment
- Short-term benefits:Allows easier customization because the design is more responsive to dynamic processes
- Long-term benefits:Allows system and organizational health to become more accurate and synchronized closer to real-time changes
EDA and SOA together
Unlike a request/reply system, where callers must explicitly request information, an event-driven architecture (EDA) provides a mechanism for systems to respond dynamically as events occur. In an EDA, events are published by event producers, and event consumers receive events as they happen.
Business systems benefit from the features of both an SOA and an EDA, since an EDA can trigger event consumers as events happen and loosely coupled services can be quickly accessed and queried from those same consumers.
For systems to be most responsive, they must be able to quickly determine the necessary actions when events are triggered. To this end, events should be published and consumed across all boundaries of the SOA, including the layers of the architectural stack and across physical tiers.
Figure 1 illustrates possible events that can be triggered across layers of an architectural stack:
In the context of Figure 1, an event can be defined as any published change in a system, platform, component, business, or application process. Events can be high-level and business-oriented, or low-level and technical in character. Because events can be transmitted and received, event-aware applications and services can respond to the underlying changes as needed.
Event taxonomies and causality
The secret to understanding a given event is to know its cause at the time the event occurred, knowledge often referred to as event causality.Event causality is typically divided into two basic categories:
- Horizontal causality:Both the event's source and cause reside on the same conceptual layer in the architectural stack
- Vertical causality:Both the event's source and cause reside on different conceptual layers in the architectural stack
Vertical causality implies an event taxonomy that remains somewhat constant across different layers of a system, as illustrated by the following list:
- Lifecycle events:Signify changes in an entity's lifecycle, such as stopping or starting a process
- Execution events:Signify runtime occurrences, such as service or component invocations
- Management events:Signify when thresholds have exceeded defined limits or ranges
Horizontal causality implies an event taxonomy that also remains somewhat constant across different layers of a system, as illustrated by the following list:
- System-layer events:Signify system-level activities, such as the creation of a file or the closing of a port
- Platform-layer events:Signify platform-level activities, such as the modification of a datasource or the addition of a new service
- Component-layer events:Signify component-level activities, such as the transformation of a view object or a state-machine transition
- Business-layer events:Signify business-level activities, such as the creation of a user or the removal of an account
- Application-layer events:Signify application-level activities, such as a premium increase or a quote submission
The benefits of event-driven communication within an SOA are currently being realized by a number of ESB frameworks and platforms. One of the most promising of these within the Java development realm is Mule.
Muleis an open source ESB-messaging framework and message broker, loosely based on the staged event-driven architecture (SEDA). SEDA defines a highly concurrent enterprise platform in terms of stages (self-contained application components) connected by queues. Mule uses concepts from SEDA to increase the performance of processing events.
Mule provides support for asynchronous, synchronous, and request-response event processing using disparate technologies and transports such as JMS, HTTP, email, and XML-based Remote Procedure Call. Mule can be easily embedded into any application framework and explicitly supports the Spring framework. Mule also supports dynamic, declarative, content-based, and rule-based message routing. Mule facilitates declarative and programmatic transaction support, including XA transaction support. Mule provides a representational state transfer (REST) API to provide Web-based access to events.
The Mule ESB model drives all services in a system over a decoupled, message-communication backbone. Services registered with the bus have no knowledge of other registered services; therefore, each service is concerned with processing only the events it receives. Mule also decouples container, transport, and transformation details from the services, allowing any kind of object to be registered as a service on the bus.
I use the Mule framework to demonstrate the concepts and ideas discussed in this article.
The Mule architecture
The Mule architecture consists primarily of the following elements:
The Universal Message Object (UMO) API
The UMO API defines the services and interactions of objects to be managed by Mule.
UMO components can be any component in the Mule system that receives, processes, and sends data as event messages.
The Mule server component is a server application launched to bootstrap the Mule environment.
The descriptor components describe a Mule UMO's attributes. New Mule UMOs can be initialized as needed from their associated descriptor. A descriptor consists of:
- The UMO component name
- The UMO component version
- The UMO component implementation class
- An exception strategy
- Inbound and outbound providers
- Inbound and outbound routers
- Receive and send endpoints
- Inbound and outbound transformers
- Miscellaneous properties
Connectors are components that provide the implementation for connecting to an external system or protocol and managing the session for that system or protocol. A connector is responsible for sending data to an external message receiver and for managing the registration and deregistration of message receivers.
Providers are components that manage the sending, receiving, and transformation of event data to and from external systems. They enable connections to external systems or other components in Mule. A provider acts as a bridge from the external system into Mule and vice versa. It is, in fact, a composite of a set of objects used to connect to and communicate with the underlying system. The elements of a provider are:
- Connector:Responsible for connecting to the underlying system
- Message receiver:Used to receive events from the system
- Connector dispatchers:Pass data to the system
- Transformers:Convert data received from the system and data being sent to the system
- Endpoint:Used as the channel address through which a connection is made
- Transaction configuration:Used to define the connection's transactional properties
Endpoint resolvers determine what method to invoke on a UMO component when the component receives an event.
Transformer components transform message or event payloads to and from different data formats. Transformers can be chained together to perform successive transforms on an event before an object receives it.
Message adapters provide a common manner in which to read disparate data from external systems.
Message receivers are listener-endpoint threads that receive data from an external system.
Message dispatchers send (synchronous) or dispatch (asynchronous) events to the underlying technology.
Message routers are components that can be configured for a UMO component to route a message to deferent providers based on the message or other configuration.
Agents are components that bind to external services such as Java Management Extension servers.
A Mule model encapsulates and manages the runtime behavior of a Mule server instance. A model consists of:
- UMO components
- An endpoint resolver
- A lifecycle-adapter factory
- A component resolver
- A pool factory
- An exception strategy
The Mule manager maintains and provides the following services:
- The interceptor stack
- A Mule model
- A Mule server
- The transaction manager
- Application properties
- The Mule configuration
The diagram in Figure 2 illustrates a high-level view of the message flow for the Mule architecture.