Scalability and SOA — Part 3 (UI Composition)
In part 2, I briefly covered Event-Driven SOA and why event messages are preferred for communication between services. I also stated that…
In part 2, I briefly covered Event-Driven SOA and why event messages are preferred for communication between services. I also stated that data should not leak out of a service boundary but didn’t answer the question of: how can we ensure services would have the data they need to carry out their tasks? This is what I will attempt to do in this post.
Only operate on local data
In order to ensure services are autonomous, they should only operate on local data. That is data they are the technical authority of. This, among other things, eliminates the need for RPC and request/response as well as Temporal and Behavioural coupling (to recap, read part 2 of this series).
As stated in part 2, data should not leak outside the service boundaries, and services communicate through events. So what does an event contain? IDs only. This has numerous benefits:
- No logical coupling: the event messages are not responsible for information from multiple services
- Easier versioning: there are no data structures to version or manage, therefore, eliminating the risk of breaking this contract between services.
- Easier testing: just as above, there is no data structure or information to test. We only ever need to test that the event has the correct IDs.
- No data leaks: as explained in part 2.
UI Composition
If services are only allowed to operate on local data, and they only share IDs, then how do we ensure that the services have the data they need when they process an event? This is achieved through UI Composition.
UI Composition is a technique by which a user interface, or a view, is composed of more than one partial view, each served up by a service in a SOA system. It can be used when either displaying information to the user or capturing information from the user.
UI Composition disseminates the data to the services without creating any coupling among them, as they are unaware of each other. This is achieved by encapsulating the knowledge of where to send/get the data to/from as part of the client-side code of the partial view. For example, in the above figure, each service would provide partial views along with JavaScript controllers associated with them. These controllers know how to fetch/save the data contained in their corresponding views. Let's imagine the same form allowed for capturing data. Once a user enters all the data and presses save, an event is fired, and each controller handles that event and persists the data entered in the fields associated with it. This means there will be three server calls to persist the data associated with services A, B and C. If this is raising alarm bells in your head, bear with me as I will address them shortly.
Client-side generated IDs: In order to associate relevant data saved to disparate services, a shared ID is generated client side when you navigate to the composite view and then used alongside the data persisted in each service. For example, in figure 1 above, a user ID will be generated when you get to this view, and the data persisted in services A, B, and C will include this user ID, linking the information captured from the user. This ID is all the services need to identify.
Possible Concerns
Performance
if performance is a concern, then you can use client-side IT/Ops as explained by Udi Dahan. This reduces the number of calls between the client and the server but requires a bit more code in both.
Client-Side ID Generation
As developers, we are not used to generating client-side IDs, and it might sound counter-intuitive to do so. However, this is crucial when following this kind of event-driven SOA. These IDs are the glue that connects the pieces of data saved into all those independent services.
There are numerous JavaScript libraries that generate unique GUIDs; however, If, for whatever reason, you are not comfortable doing this client side, you can always create a simple service that — when called — returns a unique GUID from the server.
Error Management
What happens when the composite UI consists of multiple server calls that all pass apart from one? The commands are not handled synchronously, and if they pass validation, they are assumed successful. In other words, we only validate the commands and return. If any of the commands fail validation, we display an error message to the end user. Otherwise, a success message is displayed as valid commands should almost always be processed successfully and compensating actions are defined to deal with failures. I will go into more detail in a future post.
UI Composition Example
A user registration process may consist of creating users, creating their log-in credentials, and e-mailing the users a link to reset their passwords. Traditionally, this may be presented as a wizard with a linear, synchronous process where each step needs to be completed in order to move to the next step. In Event-Driven SOA with Composite UIs, the create user screen is an amalgamation of the users and credentials services, each providing inputs to capture the data they are responsible for. A user ID is generated client-side when the end-user navigates to this screen. Subsequently, the users’ details and credentials are asynchronously and simultaneously saved to their corresponding services along with the user ID. Once the user is created by the users service, a UserCreated event containing only the user ID is fired. The credentials service, which is subscribed to this event, then uses the user ID contained in the event to retrieve the user’s credentials. It then creates a reset password token (just a GUID ID) and composes the credentials’ specific password reset e-mail. It subsequently sends a command to the e-mail service to send the e-mail. The reason we can fire a UserCreated event with just an ID, is that all the user’s relevant credentials information is already saved in the credentials service through the Composite UI.
Note: As stated in the previous post in this series, you need to be judicious in your use of commands as they create coupling. However, commands are perfectly fine within the service boundaries, which is where the UI is. Hence, the above commands from the composite UI to the corresponding services are acceptable in vertical SOA services.