This blog post has been rolling around in my head for some time as I’ve continued to delve further into the depths of Fiori application development with SAPUI5. Part of my hesitancy in posting my thoughts here was due to the fact that I’ve found it rather difficult to find a set of agreed-upon best practices/design patterns for Single-Page Application (SPA) application development (SAPUI5-based or otherwise). As a result, I’m not sure if these perceived gaps truly exist, or if I’m simply trying too hard to map server-side development concepts to a SPA/client-side model. Anyway, what follows are a list of the more prominent gaps that I’ve faced of late as I’ve been working on building more complex enterprise-style apps using SAPUI5. I look forward to getting feedback from the community regarding these thoughts.
Gap #1: Is it really all about that BASE?
Going back to my days as an undergraduate Computer Science student, I’ve always thought about transactions in terms of the ACID model. As you probably know, ACID transactions have the following characteristics:
- Atomic: Here, the assumption is that transactions bundle together a number of activities as a logical unit of work. Atomicity ensures that all of these activities either succeed together, or else the transaction is rolled back.
- Consistent: One way or another, ACID transactions should leave the system in a consistent state.
- Isolated: Here, the idea is that transactions are isolated in such a way that concurrent reads/writes to the same data are prevented (e.g. through logical and/or database locks)
- Durable: Upon completion, the results of the transactions are made permanent by outputting the results to some persistent store (e.g. the system database).
These days, the new transaction model that’s getting a lot of press is the BASE transaction model (Basically Available, Soft State, Eventual Consistency). In simple terms, BASE transactions take a more optimistic view of transaction handling, effectively assuming that most transactions would implicitly operate in isolation and that consistency would eventually be obtained. This concept became popular in the NoSQL space where availability and performance were more desirable than guaranteed consistency. And, for many types of applications, this is a reasonable trade-off that most users are willing to accept.
However, since we’re talking about building apps in the SAP space, I would submit that there will be lots of times where the BASE transaction model simply doesn’t work for enterprise apps. For example, our company uses a fairly prominent SaaS provider (not SAP) to manage our consultant time sheets. Here, we’ve been able to identify multiple places where two or more users could overwrite the same piece of data without either user even realizing it. Imagine trying to justify such an occurrence to SAP users who have long relied on rock solid consistency with classic GUI transactions!
Limitations of the BASE Transaction Model
While this post might appear to be an indictment on the BASE transaction model, that’s not my intention here. I think it definitely has a place for many types of applications. The problem(s) I see when it comes to the development of complex CRUD applications is that the onus for maintaining consistency falls to the application developer.
To put this into perspective, consider the time sheet application noted earlier. Had the SaaS vendor wanted to, they could have tracked changes to time sheets via their RESTful APIs using ETags, etc. In this case, if two users were to access the same time sheet and they both tried to make updates, it would have been possible for the system to alert the second user to the fact that their data was stale before overwriting the first user’s changes. This is all very doable and SAP Gateway even makes it pretty easy to generate ETags with OData services, for instance. The challenge I see is that the UI layer has to assume a lot of responsibility to a) track the changes and b) figure out what to do whenever there are collisions. For instance, in the example scenario would the system have to re-fetch the updated time sheet and somehow try to merge the 2nd user’s changes? This all gets very messy in a hurry. In my mind, it’s much better to place a lock on the front door and never allow two or more users to get into this mess in the first place.
With that being said, my open question to the community is this: does it make sense to introduce some kind of RESTful service that provides access to lock resources (e.g. enqueue locks on the AS ABAP)? Here, the idea is to use this service to implement logical locks that protected resources from concurrent access, etc. The downside on this of course is dealing with situations where auto-lock clearing fails for some reason and you end up with invalid locks. This issue would likely be more pronounced than what you currently see with server-side apps such as Web Dynpro ABAP or BSPs, but what’s the alternative? Is there a best practice out there to deal with concurrent access issues in client-side SPA apps?
Gap #2: What about resource-level security?
As the SAPUI5 library has matured, we’ve seen the concept of routing expand/evolve into something that’s pretty robust. However, to date, the examples I’ve seen have implicitly assumed that a user has access to any of the routes defined within the SPA app. Now, part of this might be due to the fact that the Fiori 1-1-3 principle (e.g. 1 user, 1 scenario, 3 screens) sort of implies that perhaps an application shouldn’t require resource-level restrictions, but I think the use case still exists. In server-side models, we could easily restrict access to specific pages and even content within those pages.
So, my question here is this: what is the best practice for controlling this kind of access? Do we implement a RESTful service to perform authorization checks and integrate it into our router/controller logic? Something else?
Gap #3: What about Dynamic Field Properties/Validations?
Frequently, I run into use cases where there are requirements to conditionally show/hide and/or enable/disable fields. In these scenarios, it’s often the case that such rules are dynamic and may involve some fairly complex business logic. In keeping with the old adage of “find what varies, and encapsulate it”, I think it’s obvious that we need to abstract this functionality somehow. The question is where?
In server-side models, this kind of logic typically exists centrally within business objects in the model layer. For example, I’ve recently worked pretty extensively with Web Dynpro/FPM applications which are built on top of the Business Object Processing Framework (BOPF). Here, the BOPF object model defines node-level properties which can be bound to UI elements quite easily. At runtime, user input is constantly being synchronized with BO instances in shared memory via server roundtrips and so the property updates can take place centrally without requiring any custom logic in the UI/view layer. The downside from a UX perspective is that the user has to incur a server roundtrip for some of the UI updates to take place.
If we contrast this with client-side models, we have RESTful/OData service-based models which supply the data, but there’s no natural mechanism for synchronizing field properties. In a couple of instances, I’ve created separate RESTful services to feed this data to the UI layer out-of-band, but that feels like a hack. On the other hand, I’m very hesitant to integrate this logic into SAPUI5 controller classes, etc. If I go to the trouble of building out things like mandatory field checks, consistency checks, and so forth within my OData services, I don’t want to have to replicate that same logic upstream.
Has anyone encountered/developed a framework for something like this? This feels to me like an important gap that calls for framework-level support. Another thought that popped into my head is whether or not this is another kind of operation that should be provided in OData? In other words, besides your normal CRUD+Q, you have CRUD+Q+V, where V stands for validation of a given entity(set). This might be too crazy though.
In closing, I hope the community will (forgive?) indulge me with my open questions here. I hope this post might spark some lively conversation and maybe get to the bottom of some pesky issues that I know I’m not alone on.